diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 70b496dc18a0..5429d2645bbc 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -510,14 +510,16 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) qh_lines (ehci, qh, &next, &size); - if (ehci->async_unlink && size > 0) { + if (!list_empty(&ehci->async_unlink) && size > 0) { temp = scnprintf(next, size, "\nunlink =\n"); size -= temp; next += temp; - for (qh = ehci->async_unlink; size > 0 && qh; - qh = qh->unlink_next) - qh_lines (ehci, qh, &next, &size); + list_for_each_entry(qh, &ehci->async_unlink, unlink_node) { + if (size <= 0) + break; + qh_lines(ehci, qh, &next, &size); + } } spin_unlock_irqrestore (&ehci->lock, flags); @@ -814,9 +816,10 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } } - if (ehci->async_unlink) { + if (!list_empty(&ehci->async_unlink)) { temp = scnprintf(next, size, "async unlink qh %p\n", - ehci->async_unlink); + list_first_entry(&ehci->async_unlink, + struct ehci_qh, unlink_node)); size -= temp; next += temp; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 87fe3daaa092..b32323ca07d3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -482,6 +482,9 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->async_unlink); + INIT_LIST_HEAD(&ehci->async_iaa); + INIT_LIST_HEAD(&ehci->intr_unlink); INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list); @@ -749,7 +752,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->async_iaa) + if (!list_empty(&ehci->async_iaa)) COUNT(ehci->stats.iaa); end_unlink_async(ehci); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index fca741dbf9df..4a01367bb2a0 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -958,8 +958,9 @@ static void disable_async(struct ehci_hcd *ehci) if (--ehci->async_count) return; - /* The async schedule and async_unlink list are supposed to be empty */ - WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink); + /* The async schedule and unlink lists are supposed to be empty */ + WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) || + !list_empty(&ehci->async_iaa)); /* Don't turn off the schedule until ASS is 1 */ ehci_poll_ASS(ehci); @@ -1150,11 +1151,7 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) /* Add to the end of the list of QHs waiting for the next IAAD */ qh->qh_state = QH_STATE_UNLINK_WAIT; - if (ehci->async_unlink) - ehci->async_unlink_last->unlink_next = qh; - else - ehci->async_unlink = qh; - ehci->async_unlink_last = qh; + list_add_tail(&qh->unlink_node, &ehci->async_unlink); /* Unlink it from the schedule */ prev = ehci->async; @@ -1173,15 +1170,14 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) * Do nothing if an IAA cycle is already running or * if one will be started shortly. */ - if (ehci->async_iaa || ehci->async_unlinking) + if (!list_empty(&ehci->async_iaa) || ehci->async_unlinking) return; /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* Do all the waiting QHs */ - ehci->async_iaa = ehci->async_unlink; - ehci->async_unlink = NULL; + list_splice_tail_init(&ehci->async_unlink, &ehci->async_iaa); if (!nested) /* Avoid recursion */ end_unlink_async(ehci); @@ -1191,20 +1187,18 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) struct ehci_qh *qh; /* Do only the first waiting QH (nVidia bug?) */ - qh = ehci->async_unlink; + qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, + unlink_node); /* * Intel (?) bug: The HC can write back the overlay region * even after the IAA interrupt occurs. In self-defense, * always go through two IAA cycles for each QH. */ - if (qh->qh_state == QH_STATE_UNLINK_WAIT) { + if (qh->qh_state == QH_STATE_UNLINK_WAIT) qh->qh_state = QH_STATE_UNLINK; - } else { - ehci->async_iaa = qh; - ehci->async_unlink = qh->unlink_next; - qh->unlink_next = NULL; - } + else + list_move_tail(&qh->unlink_node, &ehci->async_iaa); /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1229,10 +1223,10 @@ static void end_unlink_async(struct ehci_hcd *ehci) /* Process the idle QHs */ restart: ehci->async_unlinking = true; - while (ehci->async_iaa) { - qh = ehci->async_iaa; - ehci->async_iaa = qh->unlink_next; - qh->unlink_next = NULL; + while (!list_empty(&ehci->async_iaa)) { + qh = list_first_entry(&ehci->async_iaa, struct ehci_qh, + unlink_node); + list_del(&qh->unlink_node); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; @@ -1247,7 +1241,7 @@ static void end_unlink_async(struct ehci_hcd *ehci) ehci->async_unlinking = false; /* Start a new IAA cycle if any QHs are waiting for it */ - if (ehci->async_unlink) { + if (!list_empty(&ehci->async_unlink)) { start_iaa_cycle(ehci, true); if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) goto restart; @@ -1276,7 +1270,8 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } /* If nothing else is being unlinked, unlink the last empty QH */ - if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { + if (list_empty(&ehci->async_iaa) && list_empty(&ehci->async_unlink) && + qh_to_unlink) { start_unlink_async(ehci, qh_to_unlink); --count; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e7a2dbd27b1e..c833febf8df0 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -620,17 +620,13 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->unlink_cycle = ehci->intr_unlink_cycle; /* New entries go at the end of the intr_unlink list */ - if (ehci->intr_unlink) - ehci->intr_unlink_last->unlink_next = qh; - else - ehci->intr_unlink = qh; - ehci->intr_unlink_last = qh; + list_add_tail(&qh->unlink_node, &ehci->intr_unlink); if (ehci->intr_unlinking) ; /* Avoid recursive calls */ else if (ehci->rh_state < EHCI_RH_RUNNING) ehci_handle_intr_unlinks(ehci); - else if (ehci->intr_unlink == qh) { + else if (ehci->intr_unlink.next == &qh->unlink_node) { ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); ++ehci->intr_unlink_cycle; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index e7363332887e..f63a98353efd 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -229,18 +229,19 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) * process all the QHs on the list. */ ehci->intr_unlinking = true; - while (ehci->intr_unlink) { - struct ehci_qh *qh = ehci->intr_unlink; + while (!list_empty(&ehci->intr_unlink)) { + struct ehci_qh *qh; + qh = list_first_entry(&ehci->intr_unlink, struct ehci_qh, + unlink_node); if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) break; - ehci->intr_unlink = qh->unlink_next; - qh->unlink_next = NULL; + list_del(&qh->unlink_node); end_unlink_intr(ehci, qh); } /* Handle remaining entries later */ - if (ehci->intr_unlink) { + if (!list_empty(&ehci->intr_unlink)) { ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); ++ehci->intr_unlink_cycle; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6815209511aa..13f67041502e 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -128,9 +128,8 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ - struct ehci_qh *async_unlink; - struct ehci_qh *async_unlink_last; - struct ehci_qh *async_iaa; + struct list_head async_unlink; + struct list_head async_iaa; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ @@ -143,8 +142,7 @@ struct ehci_hcd { /* one per controller */ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ - struct ehci_qh *intr_unlink; - struct ehci_qh *intr_unlink_last; + struct list_head intr_unlink; unsigned intr_unlink_cycle; unsigned now_frame; /* frame from HC hardware */ unsigned last_iso_frame; /* last frame scanned for iso */ @@ -380,7 +378,7 @@ struct ehci_qh { struct list_head qtd_list; /* sw qtd list */ struct list_head intr_node; /* list of intr QHs */ struct ehci_qtd *dummy; - struct ehci_qh *unlink_next; /* next on unlink list */ + struct list_head unlink_node; unsigned unlink_cycle;