diff options
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 67 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 40 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
4 files changed, 61 insertions, 53 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f2ceb5fdbeb7..025d33313681 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -619,9 +619,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) | |||
619 | unsigned i = HCS_N_PORTS (ehci->hcs_params); | 619 | unsigned i = HCS_N_PORTS (ehci->hcs_params); |
620 | 620 | ||
621 | /* resume root hub? */ | 621 | /* resume root hub? */ |
622 | status = readl (&ehci->regs->command); | 622 | if (!(readl(&ehci->regs->command) & CMD_RUN)) |
623 | if (!(status & CMD_RUN)) | 623 | usb_hcd_resume_root_hub(hcd); |
624 | writel (status | CMD_RUN, &ehci->regs->command); | ||
625 | 624 | ||
626 | while (i--) { | 625 | while (i--) { |
627 | int pstatus = readl (&ehci->regs->port_status [i]); | 626 | int pstatus = readl (&ehci->regs->port_status [i]); |
@@ -638,7 +637,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) | |||
638 | */ | 637 | */ |
639 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | 638 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); |
640 | ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); | 639 | ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); |
641 | usb_hcd_resume_root_hub(hcd); | ||
642 | } | 640 | } |
643 | } | 641 | } |
644 | 642 | ||
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 0a56dfa2745d..bfe5f307cba6 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -34,6 +34,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
34 | { | 34 | { |
35 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 35 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
36 | int port; | 36 | int port; |
37 | int mask; | ||
37 | 38 | ||
38 | if (time_before (jiffies, ehci->next_statechange)) | 39 | if (time_before (jiffies, ehci->next_statechange)) |
39 | msleep(5); | 40 | msleep(5); |
@@ -51,14 +52,25 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
51 | ehci->reclaim_ready = 1; | 52 | ehci->reclaim_ready = 1; |
52 | ehci_work(ehci); | 53 | ehci_work(ehci); |
53 | 54 | ||
54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ | 55 | /* Unlike other USB host controller types, EHCI doesn't have |
56 | * any notion of "global" or bus-wide suspend. The driver has | ||
57 | * to manually suspend all the active unsuspended ports, and | ||
58 | * then manually resume them in the bus_resume() routine. | ||
59 | */ | ||
60 | ehci->bus_suspended = 0; | ||
55 | while (port--) { | 61 | while (port--) { |
56 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 62 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
57 | u32 t1 = readl (reg) & ~PORT_RWC_BITS; | 63 | u32 t1 = readl (reg) & ~PORT_RWC_BITS; |
58 | u32 t2 = t1; | 64 | u32 t2 = t1; |
59 | 65 | ||
60 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) | 66 | /* keep track of which ports we suspend */ |
67 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && | ||
68 | !(t1 & PORT_SUSPEND)) { | ||
61 | t2 |= PORT_SUSPEND; | 69 | t2 |= PORT_SUSPEND; |
70 | set_bit(port, &ehci->bus_suspended); | ||
71 | } | ||
72 | |||
73 | /* enable remote wakeup on all ports */ | ||
62 | if (device_may_wakeup(&hcd->self.root_hub->dev)) | 74 | if (device_may_wakeup(&hcd->self.root_hub->dev)) |
63 | t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; | 75 | t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; |
64 | else | 76 | else |
@@ -76,6 +88,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
76 | ehci_halt (ehci); | 88 | ehci_halt (ehci); |
77 | hcd->state = HC_STATE_SUSPENDED; | 89 | hcd->state = HC_STATE_SUSPENDED; |
78 | 90 | ||
91 | /* allow remote wakeup */ | ||
92 | mask = INTR_MASK; | ||
93 | if (!device_may_wakeup(&hcd->self.root_hub->dev)) | ||
94 | mask &= ~STS_PCD; | ||
95 | writel(mask, &ehci->regs->intr_enable); | ||
96 | readl(&ehci->regs->intr_enable); | ||
97 | |||
79 | ehci->next_statechange = jiffies + msecs_to_jiffies(10); | 98 | ehci->next_statechange = jiffies + msecs_to_jiffies(10); |
80 | spin_unlock_irq (&ehci->lock); | 99 | spin_unlock_irq (&ehci->lock); |
81 | return 0; | 100 | return 0; |
@@ -88,7 +107,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
88 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 107 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
89 | u32 temp; | 108 | u32 temp; |
90 | int i; | 109 | int i; |
91 | int intr_enable; | ||
92 | 110 | ||
93 | if (time_before (jiffies, ehci->next_statechange)) | 111 | if (time_before (jiffies, ehci->next_statechange)) |
94 | msleep(5); | 112 | msleep(5); |
@@ -100,31 +118,30 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
100 | * the last user of the controller, not reset/pm hardware keeping | 118 | * the last user of the controller, not reset/pm hardware keeping |
101 | * state we gave to it. | 119 | * state we gave to it. |
102 | */ | 120 | */ |
121 | temp = readl(&ehci->regs->intr_enable); | ||
122 | ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); | ||
103 | 123 | ||
104 | /* re-init operational registers in case we lost power */ | 124 | /* at least some APM implementations will try to deliver |
105 | if (readl (&ehci->regs->intr_enable) == 0) { | 125 | * IRQs right away, so delay them until we're ready. |
106 | /* at least some APM implementations will try to deliver | 126 | */ |
107 | * IRQs right away, so delay them until we're ready. | 127 | writel(0, &ehci->regs->intr_enable); |
108 | */ | 128 | |
109 | intr_enable = 1; | 129 | /* re-init operational registers */ |
110 | writel (0, &ehci->regs->segment); | 130 | writel(0, &ehci->regs->segment); |
111 | writel (ehci->periodic_dma, &ehci->regs->frame_list); | 131 | writel(ehci->periodic_dma, &ehci->regs->frame_list); |
112 | writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); | 132 | writel((u32) ehci->async->qh_dma, &ehci->regs->async_next); |
113 | } else | ||
114 | intr_enable = 0; | ||
115 | ehci_dbg(ehci, "resume root hub%s\n", | ||
116 | intr_enable ? " after power loss" : ""); | ||
117 | 133 | ||
118 | /* restore CMD_RUN, framelist size, and irq threshold */ | 134 | /* restore CMD_RUN, framelist size, and irq threshold */ |
119 | writel (ehci->command, &ehci->regs->command); | 135 | writel (ehci->command, &ehci->regs->command); |
120 | 136 | ||
121 | /* take ports out of suspend */ | 137 | /* manually resume the ports we suspended during bus_suspend() */ |
122 | i = HCS_N_PORTS (ehci->hcs_params); | 138 | i = HCS_N_PORTS (ehci->hcs_params); |
123 | while (i--) { | 139 | while (i--) { |
124 | temp = readl (&ehci->regs->port_status [i]); | 140 | temp = readl (&ehci->regs->port_status [i]); |
125 | temp &= ~(PORT_RWC_BITS | 141 | temp &= ~(PORT_RWC_BITS |
126 | | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); | 142 | | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); |
127 | if (temp & PORT_SUSPEND) { | 143 | if (test_bit(i, &ehci->bus_suspended) && |
144 | (temp & PORT_SUSPEND)) { | ||
128 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | 145 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); |
129 | temp |= PORT_RESUME; | 146 | temp |= PORT_RESUME; |
130 | } | 147 | } |
@@ -134,11 +151,12 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
134 | mdelay (20); | 151 | mdelay (20); |
135 | while (i--) { | 152 | while (i--) { |
136 | temp = readl (&ehci->regs->port_status [i]); | 153 | temp = readl (&ehci->regs->port_status [i]); |
137 | if ((temp & PORT_SUSPEND) == 0) | 154 | if (test_bit(i, &ehci->bus_suspended) && |
138 | continue; | 155 | (temp & PORT_SUSPEND)) { |
139 | temp &= ~(PORT_RWC_BITS | PORT_RESUME); | 156 | temp &= ~(PORT_RWC_BITS | PORT_RESUME); |
140 | writel (temp, &ehci->regs->port_status [i]); | 157 | writel (temp, &ehci->regs->port_status [i]); |
141 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); | 158 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); |
159 | } | ||
142 | } | 160 | } |
143 | (void) readl (&ehci->regs->command); | 161 | (void) readl (&ehci->regs->command); |
144 | 162 | ||
@@ -157,8 +175,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
157 | hcd->state = HC_STATE_RUNNING; | 175 | hcd->state = HC_STATE_RUNNING; |
158 | 176 | ||
159 | /* Now we can safely re-enable irqs */ | 177 | /* Now we can safely re-enable irqs */ |
160 | if (intr_enable) | 178 | writel(INTR_MASK, &ehci->regs->intr_enable); |
161 | writel (INTR_MASK, &ehci->regs->intr_enable); | ||
162 | 179 | ||
163 | spin_unlock_irq (&ehci->lock); | 180 | spin_unlock_irq (&ehci->lock); |
164 | return 0; | 181 | return 0; |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index e51c1ed81ac4..4bc7970ba3ef 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -257,9 +257,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message) | |||
257 | static int ehci_pci_resume(struct usb_hcd *hcd) | 257 | static int ehci_pci_resume(struct usb_hcd *hcd) |
258 | { | 258 | { |
259 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 259 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
260 | unsigned port; | ||
261 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | 260 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); |
262 | int retval = -EINVAL; | ||
263 | 261 | ||
264 | // maybe restore FLADJ | 262 | // maybe restore FLADJ |
265 | 263 | ||
@@ -269,27 +267,19 @@ static int ehci_pci_resume(struct usb_hcd *hcd) | |||
269 | /* Mark hardware accessible again as we are out of D3 state by now */ | 267 | /* Mark hardware accessible again as we are out of D3 state by now */ |
270 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 268 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
271 | 269 | ||
272 | /* If CF is clear, we lost PCI Vaux power and need to restart. */ | 270 | /* If CF is still set, we maintained PCI Vaux power. |
273 | if (readl(&ehci->regs->configured_flag) != FLAG_CF) | 271 | * Just undo the effect of ehci_pci_suspend(). |
274 | goto restart; | ||
275 | |||
276 | /* If any port is suspended (or owned by the companion), | ||
277 | * we know we can/must resume the HC (and mustn't reset it). | ||
278 | * We just defer that to the root hub code. | ||
279 | */ | 272 | */ |
280 | for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) { | 273 | if (readl(&ehci->regs->configured_flag) == FLAG_CF) { |
281 | u32 status; | 274 | int mask = INTR_MASK; |
282 | port--; | 275 | |
283 | status = readl(&ehci->regs->port_status [port]); | 276 | if (!device_may_wakeup(&hcd->self.root_hub->dev)) |
284 | if (!(status & PORT_POWER)) | 277 | mask &= ~STS_PCD; |
285 | continue; | 278 | writel(mask, &ehci->regs->intr_enable); |
286 | if (status & (PORT_SUSPEND | PORT_RESUME | PORT_OWNER)) { | 279 | readl(&ehci->regs->intr_enable); |
287 | usb_hcd_resume_root_hub(hcd); | 280 | return 0; |
288 | return 0; | ||
289 | } | ||
290 | } | 281 | } |
291 | 282 | ||
292 | restart: | ||
293 | ehci_dbg(ehci, "lost power, restarting\n"); | 283 | ehci_dbg(ehci, "lost power, restarting\n"); |
294 | usb_root_hub_lost_power(hcd->self.root_hub); | 284 | usb_root_hub_lost_power(hcd->self.root_hub); |
295 | 285 | ||
@@ -307,13 +297,15 @@ restart: | |||
307 | ehci_work(ehci); | 297 | ehci_work(ehci); |
308 | spin_unlock_irq(&ehci->lock); | 298 | spin_unlock_irq(&ehci->lock); |
309 | 299 | ||
310 | /* restart; khubd will disconnect devices */ | ||
311 | retval = ehci_run(hcd); | ||
312 | |||
313 | /* here we "know" root ports should always stay powered */ | 300 | /* here we "know" root ports should always stay powered */ |
314 | ehci_port_power(ehci, 1); | 301 | ehci_port_power(ehci, 1); |
315 | 302 | ||
316 | return retval; | 303 | writel(ehci->command, &ehci->regs->command); |
304 | writel(FLAG_CF, &ehci->regs->configured_flag); | ||
305 | readl(&ehci->regs->command); /* unblock posted writes */ | ||
306 | |||
307 | hcd->state = HC_STATE_SUSPENDED; | ||
308 | return 0; | ||
317 | } | 309 | } |
318 | #endif | 310 | #endif |
319 | 311 | ||
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bbc3082a73d7..74dbc6c8228f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -74,6 +74,7 @@ struct ehci_hcd { /* one per controller */ | |||
74 | 74 | ||
75 | /* per root hub port */ | 75 | /* per root hub port */ |
76 | unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; | 76 | unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; |
77 | unsigned long bus_suspended; | ||
77 | 78 | ||
78 | /* per-HC memory pools (could be per-bus, but ...) */ | 79 | /* per-HC memory pools (could be per-bus, but ...) */ |
79 | struct dma_pool *qh_pool; /* qh per active urb */ | 80 | struct dma_pool *qh_pool; /* qh per active urb */ |