[PATCH 7/25] USB: EHCI: add new root-hub state: STOPPING

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

 



This patch (as1571) adds a new state for ehci-hcd's root hubs:
EHCI_RH_STOPPING.  This value is used at times when the root hub is
being stopped and we don't know whether or not the hardware has
finished all its DMA yet.

Although the purpose may not be apparent, this distinction will come
in useful later on.  Future patches will avoid actions that depend on
the root hub being operational (like turning on the async or periodic
schedules) when they see the state is EHCI_RH_STOPPING.

Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

---

 drivers/usb/host/ehci-dbg.c   |    2 ++
 drivers/usb/host/ehci-hcd.c   |   13 ++++++-------
 drivers/usb/host/ehci-hub.c   |    3 +--
 drivers/usb/host/ehci-q.c     |    8 ++++----
 drivers/usb/host/ehci-sched.c |    8 ++++----
 drivers/usb/host/ehci.h       |    7 ++++++-
 6 files changed, 23 insertions(+), 18 deletions(-)

Index: usb-3.4/drivers/usb/host/ehci.h
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci.h
+++ usb-3.4/drivers/usb/host/ehci.h
@@ -62,10 +62,15 @@ struct ehci_stats {
 
 #define	EHCI_MAX_ROOT_PORTS	15		/* see HCS_N_PORTS */
 
+/*
+ * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
+ * controller may be doing DMA.  Lower values mean there's no DMA.
+ */
 enum ehci_rh_state {
 	EHCI_RH_HALTED,
 	EHCI_RH_SUSPENDED,
-	EHCI_RH_RUNNING
+	EHCI_RH_RUNNING,
+	EHCI_RH_STOPPING
 };
 
 struct ehci_hcd {			/* one per controller */
Index: usb-3.4/drivers/usb/host/ehci-dbg.c
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci-dbg.c
+++ usb-3.4/drivers/usb/host/ehci-dbg.c
@@ -706,6 +706,8 @@ static const char *rh_state_string(struc
 		return "suspended";
 	case EHCI_RH_RUNNING:
 		return "running";
+	case EHCI_RH_STOPPING:
+		return "stopping";
 	}
 	return "?";
 }
Index: usb-3.4/drivers/usb/host/ehci-hcd.c
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci-hcd.c
+++ usb-3.4/drivers/usb/host/ehci-hcd.c
@@ -357,10 +357,8 @@ static void ehci_quiesce (struct ehci_hc
 {
 	u32	temp;
 
-#ifdef DEBUG
 	if (ehci->rh_state != EHCI_RH_RUNNING)
-		BUG ();
-#endif
+		return;
 
 	/* wait for any schedule enables/disables to take effect */
 	temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
@@ -494,6 +492,7 @@ static void ehci_shutdown(struct usb_hcd
 	del_timer_sync(&ehci->iaa_watchdog);
 
 	spin_lock_irq(&ehci->lock);
+	ehci->rh_state = EHCI_RH_STOPPING;
 	ehci_silence_controller(ehci);
 	spin_unlock_irq(&ehci->lock);
 }
@@ -562,8 +561,7 @@ static void ehci_stop (struct usb_hcd *h
 	del_timer_sync(&ehci->iaa_watchdog);
 
 	spin_lock_irq(&ehci->lock);
-	if (ehci->rh_state == EHCI_RH_RUNNING)
-		ehci_quiesce (ehci);
+	ehci_quiesce(ehci);
 
 	ehci_silence_controller(ehci);
 	ehci_reset (ehci);
@@ -951,6 +949,7 @@ static irqreturn_t ehci_irq (struct usb_
 	/* PCI errors [4.15.2.4] */
 	if (unlikely ((status & STS_FATAL) != 0)) {
 		ehci_err(ehci, "fatal error\n");
+		ehci->rh_state = EHCI_RH_STOPPING;
 		dbg_cmd(ehci, "fatal", cmd);
 		dbg_status(ehci, "fatal", status);
 		ehci_halt(ehci);
@@ -1026,7 +1025,7 @@ static int ehci_urb_enqueue (
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	/* failfast */
-	if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink)
+	if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink)
 		end_unlink_async(ehci);
 
 	/* If the QH isn't linked then there's nothing we can do
@@ -1148,7 +1147,7 @@ rescan:
 		goto idle_timeout;
 	}
 
-	if (ehci->rh_state != EHCI_RH_RUNNING)
+	if (ehci->rh_state < EHCI_RH_RUNNING)
 		qh->qh_state = QH_STATE_IDLE;
 	switch (qh->qh_state) {
 	case QH_STATE_LINKED:
Index: usb-3.4/drivers/usb/host/ehci-hub.c
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci-hub.c
+++ usb-3.4/drivers/usb/host/ehci-hub.c
@@ -227,8 +227,7 @@ static int ehci_bus_suspend (struct usb_
 	}
 
 	/* stop schedules, clean any completed work */
-	if (ehci->rh_state == EHCI_RH_RUNNING)
-		ehci_quiesce (ehci);
+	ehci_quiesce(ehci);
 	ehci_work(ehci);
 
 	/* Unlike other USB host controller types, EHCI doesn't have
Index: usb-3.4/drivers/usb/host/ehci-q.c
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci-q.c
+++ usb-3.4/drivers/usb/host/ehci-q.c
@@ -433,7 +433,7 @@ qh_completions (struct ehci_hcd *ehci, s
 
 		/* stop scanning when we reach qtds the hc is using */
 		} else if (likely (!stopped
-				&& ehci->rh_state == EHCI_RH_RUNNING)) {
+				&& ehci->rh_state >= EHCI_RH_RUNNING)) {
 			break;
 
 		/* scan the whole queue for unlinks whenever it stops */
@@ -441,7 +441,7 @@ qh_completions (struct ehci_hcd *ehci, s
 			stopped = 1;
 
 			/* cancel everything if we halt, suspend, etc */
-			if (ehci->rh_state != EHCI_RH_RUNNING)
+			if (ehci->rh_state < EHCI_RH_RUNNING)
 				last_status = -ESHUTDOWN;
 
 			/* this qtd is active; skip it unless a previous qtd
@@ -1241,7 +1241,7 @@ static void start_unlink_async (struct e
 	wmb ();
 
 	/* If the controller isn't running, we don't have to wait for it */
-	if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) {
+	if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
 		/* if (unlikely (qh->unlink_next != 0))
 		 *	this will recurse, probably not much
 		 */
@@ -1263,7 +1263,7 @@ static void scan_async (struct ehci_hcd
 	enum ehci_timer_action	action = TIMER_IO_WATCHDOG;
 
 	timer_action_done (ehci, TIMER_ASYNC_SHRINK);
-	stopped = (ehci->rh_state != EHCI_RH_RUNNING);
+	stopped = (ehci->rh_state < EHCI_RH_RUNNING);
 
 	ehci->qh_scan_next = ehci->async->qh_next.qh;
 	while (ehci->qh_scan_next) {
Index: usb-3.4/drivers/usb/host/ehci-sched.c
===================================================================
--- usb-3.4.orig/drivers/usb/host/ehci-sched.c
+++ usb-3.4/drivers/usb/host/ehci-sched.c
@@ -2299,7 +2299,7 @@ scan_periodic (struct ehci_hcd *ehci)
 	 * Touches as few pages as possible:  cache-friendly.
 	 */
 	now_uframe = ehci->next_uframe;
-	if (ehci->rh_state == EHCI_RH_RUNNING) {
+	if (ehci->rh_state >= EHCI_RH_RUNNING) {
 		clock = ehci_read_frame_index(ehci);
 		clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
 	} else  {
@@ -2334,7 +2334,7 @@ restart:
 			union ehci_shadow	temp;
 			int			live;
 
-			live = (ehci->rh_state == EHCI_RH_RUNNING);
+			live = (ehci->rh_state >= EHCI_RH_RUNNING);
 			switch (hc32_to_cpu(ehci, type)) {
 			case Q_TYPE_QH:
 				/* handle any completions */
@@ -2459,7 +2459,7 @@ restart:
 		 * We can't advance our scan without collecting the ISO
 		 * transfers that are still pending in this frame.
 		 */
-		if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
+		if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) {
 			ehci->next_uframe = now_uframe;
 			break;
 		}
@@ -2475,7 +2475,7 @@ restart:
 		if (now_uframe == clock) {
 			unsigned	now;
 
-			if (ehci->rh_state != EHCI_RH_RUNNING
+			if (ehci->rh_state < EHCI_RH_RUNNING
 					|| ehci->periodic_sched == 0)
 				break;
 			ehci->next_uframe = now_uframe;


--
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