Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3807e26d authored by Alek Du's avatar Alek Du Committed by Greg Kroah-Hartman
Browse files

USB: EHCI: split ehci_qh 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-cache-coherence system, the entire ehci_qh is uncachable, actually
   we only need the hw part to be uncacheable. Spliting them will let the sw
   part to be cacheable.

Signed-off-by: default avatarAlek Du <alek.du@intel.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
CC: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 403dbd36
Loading
Loading
Loading
Loading
+24 −19
Original line number Original line Diff line number Diff line
@@ -134,10 +134,11 @@ dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
static void __maybe_unused
static void __maybe_unused
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
{
	struct ehci_qh_hw *hw = qh->hw;

	ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
	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->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
		qh->hw_current);
	dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
	dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
}


static void __maybe_unused
static void __maybe_unused
@@ -400,31 +401,32 @@ static void qh_lines (
	char			*next = *nextp;
	char			*next = *nextp;
	char			mark;
	char			mark;
	__le32			list_end = EHCI_LIST_END(ehci);
	__le32			list_end = EHCI_LIST_END(ehci);
	struct ehci_qh_hw	*hw = qh->hw;


	if (qh->hw_qtd_next == list_end)	/* NEC does this */
	if (hw->hw_qtd_next == list_end)	/* NEC does this */
		mark = '@';
		mark = '@';
	else
	else
		mark = token_mark(ehci, qh->hw_token);
		mark = token_mark(ehci, hw->hw_token);
	if (mark == '/') {	/* qh_alt_next controls qh advance? */
	if (mark == '/') {	/* qh_alt_next controls qh advance? */
		if ((qh->hw_alt_next & QTD_MASK(ehci))
		if ((hw->hw_alt_next & QTD_MASK(ehci))
				== ehci->async->hw_alt_next)
				== ehci->async->hw->hw_alt_next)
			mark = '#';	/* blocked */
			mark = '#';	/* blocked */
		else if (qh->hw_alt_next == list_end)
		else if (hw->hw_alt_next == list_end)
			mark = '.';	/* use hw_qtd_next */
			mark = '.';	/* use hw_qtd_next */
		/* else alt_next points to some other qtd */
		/* else alt_next points to some other qtd */
	}
	}
	scratch = hc32_to_cpup(ehci, &qh->hw_info1);
	scratch = hc32_to_cpup(ehci, &hw->hw_info1);
	hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0;
	hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
	temp = scnprintf (next, size,
	temp = scnprintf (next, size,
			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
			qh, scratch & 0x007f,
			qh, scratch & 0x007f,
			speed_char (scratch),
			speed_char (scratch),
			(scratch >> 8) & 0x000f,
			(scratch >> 8) & 0x000f,
			scratch, hc32_to_cpup(ehci, &qh->hw_info2),
			scratch, hc32_to_cpup(ehci, &hw->hw_info2),
			hc32_to_cpup(ehci, &qh->hw_token), mark,
			hc32_to_cpup(ehci, &hw->hw_token), mark,
			(cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
			(cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
				? "data1" : "data0",
				? "data1" : "data0",
			(hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f);
			(hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
	size -= temp;
	size -= temp;
	next += temp;
	next += temp;


@@ -435,10 +437,10 @@ static void qh_lines (
		mark = ' ';
		mark = ' ';
		if (hw_curr == td->qtd_dma)
		if (hw_curr == td->qtd_dma)
			mark = '*';
			mark = '*';
		else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
		else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
			mark = '+';
			mark = '+';
		else if (QTD_LENGTH (scratch)) {
		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 = '#';
				mark = '#';
			else if (td->hw_alt_next != list_end)
			else if (td->hw_alt_next != list_end)
				mark = '/';
				mark = '/';
@@ -550,12 +552,15 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
		next += temp;
		next += temp;


		do {
		do {
			struct ehci_qh_hw *hw;

			switch (hc32_to_cpu(ehci, tag)) {
			switch (hc32_to_cpu(ehci, tag)) {
			case Q_TYPE_QH:
			case Q_TYPE_QH:
				hw = p.qh->hw;
				temp = scnprintf (next, size, " qh%d-%04x/%p",
				temp = scnprintf (next, size, " qh%d-%04x/%p",
						p.qh->period,
						p.qh->period,
						hc32_to_cpup(ehci,
						hc32_to_cpup(ehci,
								&p.qh->hw_info2)
							&hw->hw_info2)
							/* uframe masks */
							/* uframe masks */
							& (QH_CMASK | QH_SMASK),
							& (QH_CMASK | QH_SMASK),
						p.qh);
						p.qh);
@@ -576,7 +581,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
				/* show more info the first time around */
				/* show more info the first time around */
				if (temp == seen_count) {
				if (temp == seen_count) {
					u32	scratch = hc32_to_cpup(ehci,
					u32	scratch = hc32_to_cpup(ehci,
							&p.qh->hw_info1);
							&hw->hw_info1);
					struct ehci_qtd	*qtd;
					struct ehci_qtd	*qtd;
					char		*type = "";
					char		*type = "";


@@ -609,7 +614,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
				} else
				} else
					temp = 0;
					temp = 0;
				if (p.qh) {
				if (p.qh) {
					tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
					tag = Q_NEXT_TYPE(ehci, hw->hw_next);
					p = p.qh->qh_next;
					p = p.qh->qh_next;
				}
				}
				break;
				break;
+8 −6
Original line number Original line Diff line number Diff line
@@ -507,6 +507,7 @@ static int ehci_init(struct usb_hcd *hcd)
	u32			temp;
	u32			temp;
	int			retval;
	int			retval;
	u32			hcc_params;
	u32			hcc_params;
	struct ehci_qh_hw	*hw;


	spin_lock_init(&ehci->lock);
	spin_lock_init(&ehci->lock);


@@ -550,12 +551,13 @@ static int ehci_init(struct usb_hcd *hcd)
	 * from automatically advancing to the next td after short reads.
	 * from automatically advancing to the next td after short reads.
	 */
	 */
	ehci->async->qh_next.qh = NULL;
	ehci->async->qh_next.qh = NULL;
	ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
	hw = ehci->async->hw;
	ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
	hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
	ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
	hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
	ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
	hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
	hw->hw_qtd_next = EHCI_LIST_END(ehci);
	ehci->async->qh_state = QH_STATE_LINKED;
	ehci->async->qh_state = QH_STATE_LINKED;
	ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
	hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);


	/* clear interrupt enables, set irq latency */
	/* clear interrupt enables, set irq latency */
	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -985,7 +987,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
	/* endpoints can be iso streams.  for now, we don't
	/* endpoints can be iso streams.  for now, we don't
	 * accelerate iso completions ... so spin a while.
	 * accelerate iso completions ... so spin a while.
	 */
	 */
	if (qh->hw_info1 == 0) {
	if (qh->hw->hw_info1 == 0) {
		ehci_vdbg (ehci, "iso delay\n");
		ehci_vdbg (ehci, "iso delay\n");
		goto idle_timeout;
		goto idle_timeout;
	}
	}
+17 −9
Original line number Original line Diff line number Diff line
@@ -75,7 +75,8 @@ static void qh_destroy(struct ehci_qh *qh)
	}
	}
	if (qh->dummy)
	if (qh->dummy)
		ehci_qtd_free (ehci, 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)
static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
@@ -83,12 +84,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
	struct ehci_qh		*qh;
	struct ehci_qh		*qh;
	dma_addr_t		dma;
	dma_addr_t		dma;


	qh = (struct ehci_qh *)
	qh = kzalloc(sizeof *qh, GFP_ATOMIC);
		dma_pool_alloc (ehci->qh_pool, flags, &dma);
	if (!qh)
	if (!qh)
		return qh;
		goto done;

	qh->hw = (struct ehci_qh_hw *)
	memset (qh, 0, sizeof *qh);
		dma_pool_alloc(ehci->qh_pool, flags, &dma);
	if (!qh->hw)
		goto fail;
	memset(qh->hw, 0, sizeof *qh->hw);
	qh->refcount = 1;
	qh->refcount = 1;
	qh->ehci = ehci;
	qh->ehci = ehci;
	qh->qh_dma = dma;
	qh->qh_dma = dma;
@@ -99,10 +102,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
	qh->dummy = ehci_qtd_alloc (ehci, flags);
	qh->dummy = ehci_qtd_alloc (ehci, flags);
	if (qh->dummy == NULL) {
	if (qh->dummy == NULL) {
		ehci_dbg (ehci, "no dummy td\n");
		ehci_dbg (ehci, "no dummy td\n");
		dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
		goto fail1;
		qh = NULL;
	}
	}
done:
	return qh;
	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) */
/* to share a qh (cpu threads, or hc) */
@@ -180,7 +188,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
	/* QHs for control/bulk/intr transfers */
	/* QHs for control/bulk/intr transfers */
	ehci->qh_pool = dma_pool_create ("ehci_qh",
	ehci->qh_pool = dma_pool_create ("ehci_qh",
			ehci_to_hcd(ehci)->self.controller,
			ehci_to_hcd(ehci)->self.controller,
			sizeof (struct ehci_qh),
			sizeof(struct ehci_qh_hw),
			32 /* byte alignment (for hw parts) */,
			32 /* byte alignment (for hw parts) */,
			4096 /* can't cross 4K */);
			4096 /* can't cross 4K */);
	if (!ehci->qh_pool) {
	if (!ehci->qh_pool) {
+28 −22
Original line number Original line Diff line number Diff line
@@ -87,31 +87,33 @@ qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
static inline void
static inline void
qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
{
{
	struct ehci_qh_hw *hw = qh->hw;

	/* writes to an active overlay are unsafe */
	/* writes to an active overlay are unsafe */
	BUG_ON(qh->qh_state != QH_STATE_IDLE);
	BUG_ON(qh->qh_state != QH_STATE_IDLE);


	qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
	hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
	qh->hw_alt_next = EHCI_LIST_END(ehci);
	hw->hw_alt_next = EHCI_LIST_END(ehci);


	/* Except for control endpoints, we make hardware maintain data
	/* Except for control endpoints, we make hardware maintain data
	 * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
	 * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
	 * and set the pseudo-toggle in udev. Only usb_clear_halt() will
	 * and set the pseudo-toggle in udev. Only usb_clear_halt() will
	 * ever clear it.
	 * ever clear it.
	 */
	 */
	if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
	if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
		unsigned	is_out, epnum;
		unsigned	is_out, epnum;


		is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
		is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
		epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
		epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
		if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
		if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
			qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
			hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
			usb_settoggle (qh->dev, epnum, is_out, 1);
			usb_settoggle (qh->dev, epnum, is_out, 1);
		}
		}
	}
	}


	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
	wmb ();
	wmb ();
	qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
	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
/* if it weren't for a common silicon quirk (writing the dummy into the qh
@@ -129,7 +131,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
		qtd = list_entry (qh->qtd_list.next,
		qtd = list_entry (qh->qtd_list.next,
				struct ehci_qtd, qtd_list);
				struct ehci_qtd, qtd_list);
		/* first qtd may already be partially processed */
		/* 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;
			qtd = NULL;
	}
	}


@@ -260,7 +262,7 @@ __acquires(ehci->lock)
		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;


		/* S-mask in a QH means it's an interrupt urb */
		/* 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) */
			/* ... update hc-wide periodic stats (for usbfs) */
			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -315,6 +317,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
	unsigned		count = 0;
	unsigned		count = 0;
	u8			state;
	u8			state;
	__le32			halt = HALT_BIT(ehci);
	__le32			halt = HALT_BIT(ehci);
	struct ehci_qh_hw	*hw = qh->hw;


	if (unlikely (list_empty (&qh->qtd_list)))
	if (unlikely (list_empty (&qh->qtd_list)))
		return count;
		return count;
@@ -392,7 +395,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
					qtd->hw_token = cpu_to_hc32(ehci,
					qtd->hw_token = cpu_to_hc32(ehci,
							token);
							token);
					wmb();
					wmb();
					qh->hw_token = cpu_to_hc32(ehci, token);
					hw->hw_token = cpu_to_hc32(ehci,
							token);
					goto retry_xacterr;
					goto retry_xacterr;
				}
				}
				stopped = 1;
				stopped = 1;
@@ -435,8 +439,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
			/* qh unlinked; token in overlay may be most current */
			/* qh unlinked; token in overlay may be most current */
			if (state == QH_STATE_IDLE
			if (state == QH_STATE_IDLE
					&& cpu_to_hc32(ehci, qtd->qtd_dma)
					&& cpu_to_hc32(ehci, qtd->qtd_dma)
						== qh->hw_current) {
						== hw->hw_current) {
				token = hc32_to_cpu(ehci, qh->hw_token);
				token = hc32_to_cpu(ehci, hw->hw_token);


				/* An unlink may leave an incomplete
				/* An unlink may leave an incomplete
				 * async transaction in the TT buffer.
				 * async transaction in the TT buffer.
@@ -449,9 +453,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
			 * patch the qh later and so that completions can't
			 * patch the qh later and so that completions can't
			 * activate it while we "know" it's stopped.
			 * activate it while we "know" it's stopped.
			 */
			 */
			if ((halt & qh->hw_token) == 0) {
			if ((halt & hw->hw_token) == 0) {
halt:
halt:
				qh->hw_token |= halt;
				hw->hw_token |= halt;
				wmb ();
				wmb ();
			}
			}
		}
		}
@@ -510,7 +514,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
	 * it after fault cleanup, or recovering from silicon wrongly
	 * it after fault cleanup, or recovering from silicon wrongly
	 * overlaying the dummy qtd (which reduces DMA chatter).
	 * overlaying the dummy qtd (which reduces DMA chatter).
	 */
	 */
	if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
	if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) {
		switch (state) {
		switch (state) {
		case QH_STATE_IDLE:
		case QH_STATE_IDLE:
			qh_refresh(ehci, qh);
			qh_refresh(ehci, qh);
@@ -528,7 +532,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
			 * except maybe high bandwidth ...
			 * except maybe high bandwidth ...
			 */
			 */
			if ((cpu_to_hc32(ehci, QH_SMASK)
			if ((cpu_to_hc32(ehci, QH_SMASK)
					& qh->hw_info2) != 0) {
					& hw->hw_info2) != 0) {
				intr_deschedule (ehci, qh);
				intr_deschedule (ehci, qh);
				(void) qh_schedule (ehci, qh);
				(void) qh_schedule (ehci, qh);
			} else
			} else
@@ -649,7 +653,7 @@ qh_urb_transaction (
		 * (this will usually be overridden later.)
		 * (this will usually be overridden later.)
		 */
		 */
		if (is_input)
		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 */
		/* qh makes control packets use qtd toggle; maybe switch it */
		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
@@ -744,6 +748,7 @@ qh_make (
	int			is_input, type;
	int			is_input, type;
	int			maxp = 0;
	int			maxp = 0;
	struct usb_tt		*tt = urb->dev->tt;
	struct usb_tt		*tt = urb->dev->tt;
	struct ehci_qh_hw	*hw;


	if (!qh)
	if (!qh)
		return qh;
		return qh;
@@ -890,8 +895,9 @@ qh_make (


	/* init as live, toggle clear, advance to dummy */
	/* init as live, toggle clear, advance to dummy */
	qh->qh_state = QH_STATE_IDLE;
	qh->qh_state = QH_STATE_IDLE;
	qh->hw_info1 = cpu_to_hc32(ehci, info1);
	hw = qh->hw;
	qh->hw_info2 = cpu_to_hc32(ehci, info2);
	hw->hw_info1 = cpu_to_hc32(ehci, info1);
	hw->hw_info2 = cpu_to_hc32(ehci, info2);
	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
	qh_refresh (ehci, qh);
	qh_refresh (ehci, qh);
	return qh;
	return qh;
@@ -933,11 +939,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)


	/* splice right after start */
	/* splice right after start */
	qh->qh_next = head->qh_next;
	qh->qh_next = head->qh_next;
	qh->hw_next = head->hw_next;
	qh->hw->hw_next = head->hw->hw_next;
	wmb ();
	wmb ();


	head->qh_next.qh = qh;
	head->qh_next.qh = qh;
	head->hw_next = dma;
	head->hw->hw_next = dma;


	qh_get(qh);
	qh_get(qh);
	qh->xacterrs = 0;
	qh->xacterrs = 0;
@@ -984,7 +990,7 @@ static struct ehci_qh *qh_append_tds (


                        /* usb_reset_device() briefly reverts to address 0 */
                        /* usb_reset_device() briefly reverts to address 0 */
                        if (usb_pipedevice (urb->pipe) == 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.
		/* just one way to queue requests: swap with the dummy qtd.
@@ -1169,7 +1175,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
	while (prev->qh_next.qh != qh)
	while (prev->qh_next.qh != qh)
		prev = prev->qh_next.qh;
		prev = prev->qh_next.qh;


	prev->hw_next = qh->hw_next;
	prev->hw->hw_next = qh->hw->hw_next;
	prev->qh_next = qh->qh_next;
	prev->qh_next = qh->qh_next;
	wmb ();
	wmb ();


+44 −22
Original line number Original line Diff line number Diff line
@@ -60,6 +60,20 @@ periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
	}
	}
}
}


static __hc32 *
shadow_next_periodic(struct ehci_hcd *ehci, union ehci_shadow *periodic,
		__hc32 tag)
{
	switch (hc32_to_cpu(ehci, tag)) {
	/* our ehci_shadow.qh is actually software part */
	case Q_TYPE_QH:
		return &periodic->qh->hw->hw_next;
	/* others are hw parts */
	default:
		return periodic->hw_next;
	}
}

/* caller must hold ehci->lock */
/* caller must hold ehci->lock */
static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
{
{
@@ -71,7 +85,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
	while (here.ptr && here.ptr != ptr) {
	while (here.ptr && here.ptr != ptr) {
		prev_p = periodic_next_shadow(ehci, prev_p,
		prev_p = periodic_next_shadow(ehci, prev_p,
				Q_NEXT_TYPE(ehci, *hw_p));
				Q_NEXT_TYPE(ehci, *hw_p));
		hw_p = here.hw_next;
		hw_p = shadow_next_periodic(ehci, &here,
				Q_NEXT_TYPE(ehci, *hw_p));
		here = *prev_p;
		here = *prev_p;
	}
	}
	/* an interrupt entry (at list end) could have been shared */
	/* an interrupt entry (at list end) could have been shared */
@@ -83,7 +98,7 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
	 */
	 */
	*prev_p = *periodic_next_shadow(ehci, &here,
	*prev_p = *periodic_next_shadow(ehci, &here,
			Q_NEXT_TYPE(ehci, *hw_p));
			Q_NEXT_TYPE(ehci, *hw_p));
	*hw_p = *here.hw_next;
	*hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p));
}
}


/* how many of the uframe's 125 usecs are allocated? */
/* how many of the uframe's 125 usecs are allocated? */
@@ -93,18 +108,20 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
	__hc32			*hw_p = &ehci->periodic [frame];
	__hc32			*hw_p = &ehci->periodic [frame];
	union ehci_shadow	*q = &ehci->pshadow [frame];
	union ehci_shadow	*q = &ehci->pshadow [frame];
	unsigned		usecs = 0;
	unsigned		usecs = 0;
	struct ehci_qh_hw	*hw;


	while (q->ptr) {
	while (q->ptr) {
		switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
		switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
		case Q_TYPE_QH:
		case Q_TYPE_QH:
			hw = q->qh->hw;
			/* is it in the S-mask? */
			/* is it in the S-mask? */
			if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
			if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
				usecs += q->qh->usecs;
				usecs += q->qh->usecs;
			/* ... or C-mask? */
			/* ... or C-mask? */
			if (q->qh->hw_info2 & cpu_to_hc32(ehci,
			if (hw->hw_info2 & cpu_to_hc32(ehci,
					1 << (8 + uframe)))
					1 << (8 + uframe)))
				usecs += q->qh->c_usecs;
				usecs += q->qh->c_usecs;
			hw_p = &q->qh->hw_next;
			hw_p = &hw->hw_next;
			q = &q->qh->qh_next;
			q = &q->qh->qh_next;
			break;
			break;
		// case Q_TYPE_FSTN:
		// case Q_TYPE_FSTN:
@@ -237,10 +254,10 @@ periodic_tt_usecs (
			continue;
			continue;
		case Q_TYPE_QH:
		case Q_TYPE_QH:
			if (same_tt(dev, q->qh->dev)) {
			if (same_tt(dev, q->qh->dev)) {
				uf = tt_start_uframe(ehci, q->qh->hw_info2);
				uf = tt_start_uframe(ehci, q->qh->hw->hw_info2);
				tt_usecs[uf] += q->qh->tt_usecs;
				tt_usecs[uf] += q->qh->tt_usecs;
			}
			}
			hw_p = &q->qh->hw_next;
			hw_p = &q->qh->hw->hw_next;
			q = &q->qh->qh_next;
			q = &q->qh->qh_next;
			continue;
			continue;
		case Q_TYPE_SITD:
		case Q_TYPE_SITD:
@@ -375,6 +392,7 @@ static int tt_no_collision (
	for (; frame < ehci->periodic_size; frame += period) {
	for (; frame < ehci->periodic_size; frame += period) {
		union ehci_shadow	here;
		union ehci_shadow	here;
		__hc32			type;
		__hc32			type;
		struct ehci_qh_hw	*hw;


		here = ehci->pshadow [frame];
		here = ehci->pshadow [frame];
		type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
		type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
@@ -385,17 +403,18 @@ static int tt_no_collision (
				here = here.itd->itd_next;
				here = here.itd->itd_next;
				continue;
				continue;
			case Q_TYPE_QH:
			case Q_TYPE_QH:
				hw = here.qh->hw;
				if (same_tt (dev, here.qh->dev)) {
				if (same_tt (dev, here.qh->dev)) {
					u32		mask;
					u32		mask;


					mask = hc32_to_cpu(ehci,
					mask = hc32_to_cpu(ehci,
							here.qh->hw_info2);
							hw->hw_info2);
					/* "knows" no gap is needed */
					/* "knows" no gap is needed */
					mask |= mask >> 8;
					mask |= mask >> 8;
					if (mask & uf_mask)
					if (mask & uf_mask)
						break;
						break;
				}
				}
				type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
				type = Q_NEXT_TYPE(ehci, hw->hw_next);
				here = here.qh->qh_next;
				here = here.qh->qh_next;
				continue;
				continue;
			case Q_TYPE_SITD:
			case Q_TYPE_SITD:
@@ -498,7 +517,8 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)


	dev_dbg (&qh->dev->dev,
	dev_dbg (&qh->dev->dev,
		"link qh%d-%04x/%p start %d [%d/%d us]\n",
		"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);
		qh, qh->start, qh->usecs, qh->c_usecs);


	/* high bandwidth, or otherwise every microframe */
	/* high bandwidth, or otherwise every microframe */
@@ -517,7 +537,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
			if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
			if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
				break;
				break;
			prev = periodic_next_shadow(ehci, prev, type);
			prev = periodic_next_shadow(ehci, prev, type);
			hw_p = &here.qh->hw_next;
			hw_p = shadow_next_periodic(ehci, &here, type);
			here = *prev;
			here = *prev;
		}
		}


@@ -528,14 +548,14 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
			if (qh->period > here.qh->period)
			if (qh->period > here.qh->period)
				break;
				break;
			prev = &here.qh->qh_next;
			prev = &here.qh->qh_next;
			hw_p = &here.qh->hw_next;
			hw_p = &here.qh->hw->hw_next;
			here = *prev;
			here = *prev;
		}
		}
		/* link in this qh, unless some earlier pass did that */
		/* link in this qh, unless some earlier pass did that */
		if (qh != here.qh) {
		if (qh != here.qh) {
			qh->qh_next = here;
			qh->qh_next = here;
			if (here.qh)
			if (here.qh)
				qh->hw_next = *hw_p;
				qh->hw->hw_next = *hw_p;
			wmb ();
			wmb ();
			prev->qh = qh;
			prev->qh = qh;
			*hw_p = QH_NEXT (ehci, qh->qh_dma);
			*hw_p = QH_NEXT (ehci, qh->qh_dma);
@@ -581,7 +601,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
	dev_dbg (&qh->dev->dev,
	dev_dbg (&qh->dev->dev,
		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
		qh->period,
		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->start, qh->usecs, qh->c_usecs);


	/* qh->qh_next still "live" to HC */
	/* qh->qh_next still "live" to HC */
@@ -596,6 +616,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
{
	unsigned	wait;
	unsigned	wait;
	struct ehci_qh_hw *hw = qh->hw;


	qh_unlink_periodic (ehci, qh);
	qh_unlink_periodic (ehci, qh);


@@ -606,14 +627,14 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
	 */
	 */
	if (list_empty (&qh->qtd_list)
	if (list_empty (&qh->qtd_list)
			|| (cpu_to_hc32(ehci, QH_CMASK)
			|| (cpu_to_hc32(ehci, QH_CMASK)
					& qh->hw_info2) != 0)
					& hw->hw_info2) != 0)
		wait = 2;
		wait = 2;
	else
	else
		wait = 55;	/* worst case: 3 * 1024 */
		wait = 55;	/* worst case: 3 * 1024 */


	udelay (wait);
	udelay (wait);
	qh->qh_state = QH_STATE_IDLE;
	qh->qh_state = QH_STATE_IDLE;
	qh->hw_next = EHCI_LIST_END(ehci);
	hw->hw_next = EHCI_LIST_END(ehci);
	wmb ();
	wmb ();
}
}


@@ -739,14 +760,15 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
	unsigned	uframe;
	unsigned	uframe;
	__hc32		c_mask;
	__hc32		c_mask;
	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
	struct ehci_qh_hw	*hw = qh->hw;


	qh_refresh(ehci, qh);
	qh_refresh(ehci, qh);
	qh->hw_next = EHCI_LIST_END(ehci);
	hw->hw_next = EHCI_LIST_END(ehci);
	frame = qh->start;
	frame = qh->start;


	/* reuse the previous schedule slots, if we can */
	/* reuse the previous schedule slots, if we can */
	if (frame < qh->period) {
	if (frame < qh->period) {
		uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
		uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK);
		status = check_intr_schedule (ehci, frame, --uframe,
		status = check_intr_schedule (ehci, frame, --uframe,
				qh, &c_mask);
				qh, &c_mask);
	} else {
	} else {
@@ -784,11 +806,11 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
		qh->start = frame;
		qh->start = frame;


		/* reset S-frame and (maybe) C-frame masks */
		/* reset S-frame and (maybe) C-frame masks */
		qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
		hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
		qh->hw_info2 |= qh->period
		hw->hw_info2 |= qh->period
			? cpu_to_hc32(ehci, 1 << uframe)
			? cpu_to_hc32(ehci, 1 << uframe)
			: cpu_to_hc32(ehci, QH_SMASK);
			: cpu_to_hc32(ehci, QH_SMASK);
		qh->hw_info2 |= c_mask;
		hw->hw_info2 |= c_mask;
	} else
	} else
		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
		ehci_dbg (ehci, "reused qh %p schedule\n", qh);


@@ -2188,7 +2210,7 @@ scan_periodic (struct ehci_hcd *ehci)
			case Q_TYPE_QH:
			case Q_TYPE_QH:
				/* handle any completions */
				/* handle any completions */
				temp.qh = qh_get (q.qh);
				temp.qh = qh_get (q.qh);
				type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
				type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
				q = q.qh->qh_next;
				q = q.qh->qh_next;
				modified = qh_completions (ehci, temp.qh);
				modified = qh_completions (ehci, temp.qh);
				if (unlikely (list_empty (&temp.qh->qtd_list)))
				if (unlikely (list_empty (&temp.qh->qtd_list)))
Loading