aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-07-11 11:22:31 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-16 19:54:25 -0400
commitbf6387bcd16975ba8952b094f262a359d74e1c8a (patch)
treeac578ff256a5cd972c5866525c65e44e4360974e /drivers/usb
parentdf2022553dd8d34d49e16c19d851ea619438f0ef (diff)
USB: EHCI: use hrtimer for controller death
This patch (as1578) adds an hrtimer event to handle the death of an EHCI controller. When a controller dies, it doesn't necessarily stop running right away. The new event polls at 1-ms intervals to see when all activity has safely stopped. This replaces a busy-wait polling loop in the current code. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-hcd.c20
-rw-r--r--drivers/usb/host/ehci-timer.c26
-rw-r--r--drivers/usb/host/ehci.h2
3 files changed, 38 insertions, 10 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index edcfd2c4295e..1676c66b8530 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -888,20 +888,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
888 /* PCI errors [4.15.2.4] */ 888 /* PCI errors [4.15.2.4] */
889 if (unlikely ((status & STS_FATAL) != 0)) { 889 if (unlikely ((status & STS_FATAL) != 0)) {
890 ehci_err(ehci, "fatal error\n"); 890 ehci_err(ehci, "fatal error\n");
891 ehci->rh_state = EHCI_RH_STOPPING;
892 dbg_cmd(ehci, "fatal", cmd); 891 dbg_cmd(ehci, "fatal", cmd);
893 dbg_status(ehci, "fatal", status); 892 dbg_status(ehci, "fatal", status);
894 ehci_halt(ehci);
895dead: 893dead:
896 ehci->enabled_hrtimer_events = 0;
897 hrtimer_try_to_cancel(&ehci->hrtimer);
898 ehci_reset(ehci);
899 ehci_writel(ehci, 0, &ehci->regs->configured_flag);
900 usb_hc_died(hcd); 894 usb_hc_died(hcd);
901 /* generic layer kills/unlinks all urbs, then 895
902 * uses ehci_stop to clean up the rest 896 /* Don't let the controller do anything more */
903 */ 897 ehci->rh_state = EHCI_RH_STOPPING;
904 bh = 1; 898 ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
899 ehci_writel(ehci, ehci->command, &ehci->regs->command);
900 ehci_writel(ehci, 0, &ehci->regs->intr_enable);
901 ehci_handle_controller_death(ehci);
902
903 /* Handle completions when the controller stops */
904 bh = 0;
905 } 905 }
906 906
907 if (bh) 907 if (bh)
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index bd8b591771b0..0c5326a8883c 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
69static unsigned event_delays_ns[] = { 69static unsigned event_delays_ns[] = {
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
72 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
73 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 74 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
74 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ 75 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
@@ -193,6 +194,30 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci)
193} 194}
194 195
195 196
197/* Poll the STS_HALT status bit; see when a dead controller stops */
198static void ehci_handle_controller_death(struct ehci_hcd *ehci)
199{
200 if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {
201
202 /* Give up after a few milliseconds */
203 if (ehci->died_poll_count++ < 5) {
204 /* Try again later */
205 ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
206 return;
207 }
208 ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
209 }
210
211 /* Clean up the mess */
212 ehci->rh_state = EHCI_RH_HALTED;
213 ehci_writel(ehci, 0, &ehci->regs->configured_flag);
214 ehci_writel(ehci, 0, &ehci->regs->intr_enable);
215 ehci_work(ehci);
216
217 /* Not in process context, so don't try to reset the controller */
218}
219
220
196/* Handle unlinked interrupt QHs once they are gone from the hardware */ 221/* Handle unlinked interrupt QHs once they are gone from the hardware */
197static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) 222static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
198{ 223{
@@ -233,6 +258,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
233static void (*event_handlers[])(struct ehci_hcd *) = { 258static void (*event_handlers[])(struct ehci_hcd *) = {
234 ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ 259 ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
235 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ 260 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
261 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
236 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 262 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
237 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 263 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
238 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ 264 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index f36f1f85d7fd..6874d89b0b64 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -81,6 +81,7 @@ enum ehci_rh_state {
81enum ehci_hrtimer_event { 81enum ehci_hrtimer_event {
82 EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ 82 EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ 83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
84 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
85 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 86 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
86 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ 87 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
@@ -97,6 +98,7 @@ struct ehci_hcd { /* one per controller */
97 98
98 int PSS_poll_count; 99 int PSS_poll_count;
99 int ASS_poll_count; 100 int ASS_poll_count;
101 int died_poll_count;
100 102
101 /* glue to PCI and HCD framework */ 103 /* glue to PCI and HCD framework */
102 struct ehci_caps __iomem *caps; 104 struct ehci_caps __iomem *caps;