Skip to content

Commit

Permalink
USB: EHCI: add new root-hub state: STOPPING
Browse files Browse the repository at this point in the history
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@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jul 16, 2012
1 parent 2f5bb66 commit c0c53db
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 18 deletions.
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci)
return "suspended";
case EHCI_RH_RUNNING:
return "running";
case EHCI_RH_STOPPING:
return "stopping";
}
return "?";
}
Expand Down
13 changes: 6 additions & 7 deletions drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
{
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);
Expand Down Expand Up @@ -494,6 +492,7 @@ static void ehci_shutdown(struct usb_hcd *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);
}
Expand Down Expand Up @@ -562,8 +561,7 @@ static void ehci_stop (struct usb_hcd *hcd)
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);
Expand Down Expand Up @@ -951,6 +949,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* 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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1148,7 +1147,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
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:
Expand Down
3 changes: 1 addition & 2 deletions drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}

/* 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
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,15 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)

/* 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 */
} else {
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
Expand Down Expand Up @@ -1241,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
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
*/
Expand All @@ -1263,7 +1263,7 @@ static void scan_async (struct ehci_hcd *ehci)
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) {
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/host/ehci-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -2334,7 +2334,7 @@ scan_periodic (struct ehci_hcd *ehci)
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 */
Expand Down Expand Up @@ -2459,7 +2459,7 @@ scan_periodic (struct ehci_hcd *ehci)
* 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;
}
Expand All @@ -2475,7 +2475,7 @@ scan_periodic (struct ehci_hcd *ehci)
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;
Expand Down
7 changes: 6 additions & 1 deletion drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit c0c53db

Please sign in to comment.