diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 324 |
1 files changed, 226 insertions, 98 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index bfe5f307cba6..0d83c6df1a3b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -47,7 +47,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
47 | ehci_quiesce (ehci); | 47 | ehci_quiesce (ehci); |
48 | hcd->state = HC_STATE_QUIESCING; | 48 | hcd->state = HC_STATE_QUIESCING; |
49 | } | 49 | } |
50 | ehci->command = readl (&ehci->regs->command); | 50 | ehci->command = ehci_readl(ehci, &ehci->regs->command); |
51 | if (ehci->reclaim) | 51 | if (ehci->reclaim) |
52 | ehci->reclaim_ready = 1; | 52 | ehci->reclaim_ready = 1; |
53 | ehci_work(ehci); | 53 | ehci_work(ehci); |
@@ -60,7 +60,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
60 | ehci->bus_suspended = 0; | 60 | ehci->bus_suspended = 0; |
61 | while (port--) { | 61 | while (port--) { |
62 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 62 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
63 | u32 t1 = readl (reg) & ~PORT_RWC_BITS; | 63 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
64 | u32 t2 = t1; | 64 | u32 t2 = t1; |
65 | 65 | ||
66 | /* keep track of which ports we suspend */ | 66 | /* keep track of which ports we suspend */ |
@@ -79,7 +79,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
79 | if (t1 != t2) { | 79 | if (t1 != t2) { |
80 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", | 80 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", |
81 | port + 1, t1, t2); | 81 | port + 1, t1, t2); |
82 | writel (t2, reg); | 82 | ehci_writel(ehci, t2, reg); |
83 | } | 83 | } |
84 | } | 84 | } |
85 | 85 | ||
@@ -92,8 +92,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
92 | mask = INTR_MASK; | 92 | mask = INTR_MASK; |
93 | if (!device_may_wakeup(&hcd->self.root_hub->dev)) | 93 | if (!device_may_wakeup(&hcd->self.root_hub->dev)) |
94 | mask &= ~STS_PCD; | 94 | mask &= ~STS_PCD; |
95 | writel(mask, &ehci->regs->intr_enable); | 95 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); |
96 | readl(&ehci->regs->intr_enable); | 96 | ehci_readl(ehci, &ehci->regs->intr_enable); |
97 | 97 | ||
98 | ehci->next_statechange = jiffies + msecs_to_jiffies(10); | 98 | ehci->next_statechange = jiffies + msecs_to_jiffies(10); |
99 | spin_unlock_irq (&ehci->lock); | 99 | spin_unlock_irq (&ehci->lock); |
@@ -118,26 +118,26 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
118 | * the last user of the controller, not reset/pm hardware keeping | 118 | * the last user of the controller, not reset/pm hardware keeping |
119 | * state we gave to it. | 119 | * state we gave to it. |
120 | */ | 120 | */ |
121 | temp = readl(&ehci->regs->intr_enable); | 121 | temp = ehci_readl(ehci, &ehci->regs->intr_enable); |
122 | ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); | 122 | ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); |
123 | 123 | ||
124 | /* at least some APM implementations will try to deliver | 124 | /* at least some APM implementations will try to deliver |
125 | * IRQs right away, so delay them until we're ready. | 125 | * IRQs right away, so delay them until we're ready. |
126 | */ | 126 | */ |
127 | writel(0, &ehci->regs->intr_enable); | 127 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); |
128 | 128 | ||
129 | /* re-init operational registers */ | 129 | /* re-init operational registers */ |
130 | writel(0, &ehci->regs->segment); | 130 | ehci_writel(ehci, 0, &ehci->regs->segment); |
131 | writel(ehci->periodic_dma, &ehci->regs->frame_list); | 131 | ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); |
132 | writel((u32) ehci->async->qh_dma, &ehci->regs->async_next); | 132 | ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); |
133 | 133 | ||
134 | /* restore CMD_RUN, framelist size, and irq threshold */ | 134 | /* restore CMD_RUN, framelist size, and irq threshold */ |
135 | writel (ehci->command, &ehci->regs->command); | 135 | ehci_writel(ehci, ehci->command, &ehci->regs->command); |
136 | 136 | ||
137 | /* manually resume the ports we suspended during bus_suspend() */ | 137 | /* manually resume the ports we suspended during bus_suspend() */ |
138 | i = HCS_N_PORTS (ehci->hcs_params); | 138 | i = HCS_N_PORTS (ehci->hcs_params); |
139 | while (i--) { | 139 | while (i--) { |
140 | temp = readl (&ehci->regs->port_status [i]); | 140 | temp = ehci_readl(ehci, &ehci->regs->port_status [i]); |
141 | temp &= ~(PORT_RWC_BITS | 141 | temp &= ~(PORT_RWC_BITS |
142 | | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); | 142 | | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); |
143 | if (test_bit(i, &ehci->bus_suspended) && | 143 | if (test_bit(i, &ehci->bus_suspended) && |
@@ -145,20 +145,20 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
145 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | 145 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); |
146 | temp |= PORT_RESUME; | 146 | temp |= PORT_RESUME; |
147 | } | 147 | } |
148 | writel (temp, &ehci->regs->port_status [i]); | 148 | ehci_writel(ehci, temp, &ehci->regs->port_status [i]); |
149 | } | 149 | } |
150 | i = HCS_N_PORTS (ehci->hcs_params); | 150 | i = HCS_N_PORTS (ehci->hcs_params); |
151 | mdelay (20); | 151 | mdelay (20); |
152 | while (i--) { | 152 | while (i--) { |
153 | temp = readl (&ehci->regs->port_status [i]); | 153 | temp = ehci_readl(ehci, &ehci->regs->port_status [i]); |
154 | if (test_bit(i, &ehci->bus_suspended) && | 154 | if (test_bit(i, &ehci->bus_suspended) && |
155 | (temp & PORT_SUSPEND)) { | 155 | (temp & PORT_SUSPEND)) { |
156 | temp &= ~(PORT_RWC_BITS | PORT_RESUME); | 156 | temp &= ~(PORT_RWC_BITS | PORT_RESUME); |
157 | writel (temp, &ehci->regs->port_status [i]); | 157 | ehci_writel(ehci, temp, &ehci->regs->port_status [i]); |
158 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); | 158 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); |
159 | } | 159 | } |
160 | } | 160 | } |
161 | (void) readl (&ehci->regs->command); | 161 | (void) ehci_readl(ehci, &ehci->regs->command); |
162 | 162 | ||
163 | /* maybe re-activate the schedule(s) */ | 163 | /* maybe re-activate the schedule(s) */ |
164 | temp = 0; | 164 | temp = 0; |
@@ -168,14 +168,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
168 | temp |= CMD_PSE; | 168 | temp |= CMD_PSE; |
169 | if (temp) { | 169 | if (temp) { |
170 | ehci->command |= temp; | 170 | ehci->command |= temp; |
171 | writel (ehci->command, &ehci->regs->command); | 171 | ehci_writel(ehci, ehci->command, &ehci->regs->command); |
172 | } | 172 | } |
173 | 173 | ||
174 | ehci->next_statechange = jiffies + msecs_to_jiffies(5); | 174 | ehci->next_statechange = jiffies + msecs_to_jiffies(5); |
175 | hcd->state = HC_STATE_RUNNING; | 175 | hcd->state = HC_STATE_RUNNING; |
176 | 176 | ||
177 | /* Now we can safely re-enable irqs */ | 177 | /* Now we can safely re-enable irqs */ |
178 | writel(INTR_MASK, &ehci->regs->intr_enable); | 178 | ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); |
179 | 179 | ||
180 | spin_unlock_irq (&ehci->lock); | 180 | spin_unlock_irq (&ehci->lock); |
181 | return 0; | 181 | return 0; |
@@ -190,9 +190,107 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
190 | 190 | ||
191 | /*-------------------------------------------------------------------------*/ | 191 | /*-------------------------------------------------------------------------*/ |
192 | 192 | ||
193 | /* Display the ports dedicated to the companion controller */ | ||
194 | static ssize_t show_companion(struct class_device *class_dev, char *buf) | ||
195 | { | ||
196 | struct ehci_hcd *ehci; | ||
197 | int nports, index, n; | ||
198 | int count = PAGE_SIZE; | ||
199 | char *ptr = buf; | ||
200 | |||
201 | ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); | ||
202 | nports = HCS_N_PORTS(ehci->hcs_params); | ||
203 | |||
204 | for (index = 0; index < nports; ++index) { | ||
205 | if (test_bit(index, &ehci->companion_ports)) { | ||
206 | n = scnprintf(ptr, count, "%d\n", index + 1); | ||
207 | ptr += n; | ||
208 | count -= n; | ||
209 | } | ||
210 | } | ||
211 | return ptr - buf; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Dedicate or undedicate a port to the companion controller. | ||
216 | * Syntax is "[-]portnum", where a leading '-' sign means | ||
217 | * return control of the port to the EHCI controller. | ||
218 | */ | ||
219 | static ssize_t store_companion(struct class_device *class_dev, | ||
220 | const char *buf, size_t count) | ||
221 | { | ||
222 | struct ehci_hcd *ehci; | ||
223 | int portnum, new_owner, try; | ||
224 | u32 __iomem *status_reg; | ||
225 | u32 port_status; | ||
226 | |||
227 | ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); | ||
228 | new_owner = PORT_OWNER; /* Owned by companion */ | ||
229 | if (sscanf(buf, "%d", &portnum) != 1) | ||
230 | return -EINVAL; | ||
231 | if (portnum < 0) { | ||
232 | portnum = - portnum; | ||
233 | new_owner = 0; /* Owned by EHCI */ | ||
234 | } | ||
235 | if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) | ||
236 | return -ENOENT; | ||
237 | status_reg = &ehci->regs->port_status[--portnum]; | ||
238 | if (new_owner) | ||
239 | set_bit(portnum, &ehci->companion_ports); | ||
240 | else | ||
241 | clear_bit(portnum, &ehci->companion_ports); | ||
242 | |||
243 | /* | ||
244 | * The controller won't set the OWNER bit if the port is | ||
245 | * enabled, so this loop will sometimes require at least two | ||
246 | * iterations: one to disable the port and one to set OWNER. | ||
247 | */ | ||
248 | |||
249 | for (try = 4; try > 0; --try) { | ||
250 | spin_lock_irq(&ehci->lock); | ||
251 | port_status = ehci_readl(ehci, status_reg); | ||
252 | if ((port_status & PORT_OWNER) == new_owner | ||
253 | || (port_status & (PORT_OWNER | PORT_CONNECT)) | ||
254 | == 0) | ||
255 | try = 0; | ||
256 | else { | ||
257 | port_status ^= PORT_OWNER; | ||
258 | port_status &= ~(PORT_PE | PORT_RWC_BITS); | ||
259 | ehci_writel(ehci, port_status, status_reg); | ||
260 | } | ||
261 | spin_unlock_irq(&ehci->lock); | ||
262 | if (try > 1) | ||
263 | msleep(5); | ||
264 | } | ||
265 | return count; | ||
266 | } | ||
267 | static CLASS_DEVICE_ATTR(companion, 0644, show_companion, store_companion); | ||
268 | |||
269 | static inline void create_companion_file(struct ehci_hcd *ehci) | ||
270 | { | ||
271 | int i; | ||
272 | |||
273 | /* with integrated TT there is no companion! */ | ||
274 | if (!ehci_is_TDI(ehci)) | ||
275 | i = class_device_create_file(ehci_to_hcd(ehci)->self.class_dev, | ||
276 | &class_device_attr_companion); | ||
277 | } | ||
278 | |||
279 | static inline void remove_companion_file(struct ehci_hcd *ehci) | ||
280 | { | ||
281 | /* with integrated TT there is no companion! */ | ||
282 | if (!ehci_is_TDI(ehci)) | ||
283 | class_device_remove_file(ehci_to_hcd(ehci)->self.class_dev, | ||
284 | &class_device_attr_companion); | ||
285 | } | ||
286 | |||
287 | |||
288 | /*-------------------------------------------------------------------------*/ | ||
289 | |||
193 | static int check_reset_complete ( | 290 | static int check_reset_complete ( |
194 | struct ehci_hcd *ehci, | 291 | struct ehci_hcd *ehci, |
195 | int index, | 292 | int index, |
293 | u32 __iomem *status_reg, | ||
196 | int port_status | 294 | int port_status |
197 | ) { | 295 | ) { |
198 | if (!(port_status & PORT_CONNECT)) { | 296 | if (!(port_status & PORT_CONNECT)) { |
@@ -217,7 +315,7 @@ static int check_reset_complete ( | |||
217 | // what happens if HCS_N_CC(params) == 0 ? | 315 | // what happens if HCS_N_CC(params) == 0 ? |
218 | port_status |= PORT_OWNER; | 316 | port_status |= PORT_OWNER; |
219 | port_status &= ~PORT_RWC_BITS; | 317 | port_status &= ~PORT_RWC_BITS; |
220 | writel (port_status, &ehci->regs->port_status [index]); | 318 | ehci_writel(ehci, port_status, status_reg); |
221 | 319 | ||
222 | } else | 320 | } else |
223 | ehci_dbg (ehci, "port %d high speed\n", index + 1); | 321 | ehci_dbg (ehci, "port %d high speed\n", index + 1); |
@@ -268,22 +366,21 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
268 | /* port N changes (bit N)? */ | 366 | /* port N changes (bit N)? */ |
269 | spin_lock_irqsave (&ehci->lock, flags); | 367 | spin_lock_irqsave (&ehci->lock, flags); |
270 | for (i = 0; i < ports; i++) { | 368 | for (i = 0; i < ports; i++) { |
271 | temp = readl (&ehci->regs->port_status [i]); | 369 | temp = ehci_readl(ehci, &ehci->regs->port_status [i]); |
272 | if (temp & PORT_OWNER) { | 370 | |
273 | /* don't report this in GetPortStatus */ | 371 | /* |
274 | if (temp & PORT_CSC) { | 372 | * Return status information even for ports with OWNER set. |
275 | temp &= ~PORT_RWC_BITS; | 373 | * Otherwise khubd wouldn't see the disconnect event when a |
276 | temp |= PORT_CSC; | 374 | * high-speed device is switched over to the companion |
277 | writel (temp, &ehci->regs->port_status [i]); | 375 | * controller by the user. |
278 | } | 376 | */ |
279 | continue; | 377 | |
280 | } | ||
281 | if (!(temp & PORT_CONNECT)) | 378 | if (!(temp & PORT_CONNECT)) |
282 | ehci->reset_done [i] = 0; | 379 | ehci->reset_done [i] = 0; |
283 | if ((temp & mask) != 0 | 380 | if ((temp & mask) != 0 |
284 | || ((temp & PORT_RESUME) != 0 | 381 | || ((temp & PORT_RESUME) != 0 |
285 | && time_after (jiffies, | 382 | && time_after_eq(jiffies, |
286 | ehci->reset_done [i]))) { | 383 | ehci->reset_done[i]))) { |
287 | if (i < 7) | 384 | if (i < 7) |
288 | buf [0] |= 1 << (i + 1); | 385 | buf [0] |= 1 << (i + 1); |
289 | else | 386 | else |
@@ -345,6 +442,7 @@ static int ehci_hub_control ( | |||
345 | ) { | 442 | ) { |
346 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 443 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
347 | int ports = HCS_N_PORTS (ehci->hcs_params); | 444 | int ports = HCS_N_PORTS (ehci->hcs_params); |
445 | u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1]; | ||
348 | u32 temp, status; | 446 | u32 temp, status; |
349 | unsigned long flags; | 447 | unsigned long flags; |
350 | int retval = 0; | 448 | int retval = 0; |
@@ -373,18 +471,22 @@ static int ehci_hub_control ( | |||
373 | if (!wIndex || wIndex > ports) | 471 | if (!wIndex || wIndex > ports) |
374 | goto error; | 472 | goto error; |
375 | wIndex--; | 473 | wIndex--; |
376 | temp = readl (&ehci->regs->port_status [wIndex]); | 474 | temp = ehci_readl(ehci, status_reg); |
377 | if (temp & PORT_OWNER) | 475 | |
378 | break; | 476 | /* |
477 | * Even if OWNER is set, so the port is owned by the | ||
478 | * companion controller, khubd needs to be able to clear | ||
479 | * the port-change status bits (especially | ||
480 | * USB_PORT_FEAT_C_CONNECTION). | ||
481 | */ | ||
379 | 482 | ||
380 | switch (wValue) { | 483 | switch (wValue) { |
381 | case USB_PORT_FEAT_ENABLE: | 484 | case USB_PORT_FEAT_ENABLE: |
382 | writel (temp & ~PORT_PE, | 485 | ehci_writel(ehci, temp & ~PORT_PE, status_reg); |
383 | &ehci->regs->port_status [wIndex]); | ||
384 | break; | 486 | break; |
385 | case USB_PORT_FEAT_C_ENABLE: | 487 | case USB_PORT_FEAT_C_ENABLE: |
386 | writel((temp & ~PORT_RWC_BITS) | PORT_PEC, | 488 | ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC, |
387 | &ehci->regs->port_status [wIndex]); | 489 | status_reg); |
388 | break; | 490 | break; |
389 | case USB_PORT_FEAT_SUSPEND: | 491 | case USB_PORT_FEAT_SUSPEND: |
390 | if (temp & PORT_RESET) | 492 | if (temp & PORT_RESET) |
@@ -396,8 +498,8 @@ static int ehci_hub_control ( | |||
396 | goto error; | 498 | goto error; |
397 | /* resume signaling for 20 msec */ | 499 | /* resume signaling for 20 msec */ |
398 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | 500 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); |
399 | writel (temp | PORT_RESUME, | 501 | ehci_writel(ehci, temp | PORT_RESUME, |
400 | &ehci->regs->port_status [wIndex]); | 502 | status_reg); |
401 | ehci->reset_done [wIndex] = jiffies | 503 | ehci->reset_done [wIndex] = jiffies |
402 | + msecs_to_jiffies (20); | 504 | + msecs_to_jiffies (20); |
403 | } | 505 | } |
@@ -407,16 +509,17 @@ static int ehci_hub_control ( | |||
407 | break; | 509 | break; |
408 | case USB_PORT_FEAT_POWER: | 510 | case USB_PORT_FEAT_POWER: |
409 | if (HCS_PPC (ehci->hcs_params)) | 511 | if (HCS_PPC (ehci->hcs_params)) |
410 | writel (temp & ~(PORT_RWC_BITS | PORT_POWER), | 512 | ehci_writel(ehci, |
411 | &ehci->regs->port_status [wIndex]); | 513 | temp & ~(PORT_RWC_BITS | PORT_POWER), |
514 | status_reg); | ||
412 | break; | 515 | break; |
413 | case USB_PORT_FEAT_C_CONNECTION: | 516 | case USB_PORT_FEAT_C_CONNECTION: |
414 | writel((temp & ~PORT_RWC_BITS) | PORT_CSC, | 517 | ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, |
415 | &ehci->regs->port_status [wIndex]); | 518 | status_reg); |
416 | break; | 519 | break; |
417 | case USB_PORT_FEAT_C_OVER_CURRENT: | 520 | case USB_PORT_FEAT_C_OVER_CURRENT: |
418 | writel((temp & ~PORT_RWC_BITS) | PORT_OCC, | 521 | ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC, |
419 | &ehci->regs->port_status [wIndex]); | 522 | status_reg); |
420 | break; | 523 | break; |
421 | case USB_PORT_FEAT_C_RESET: | 524 | case USB_PORT_FEAT_C_RESET: |
422 | /* GetPortStatus clears reset */ | 525 | /* GetPortStatus clears reset */ |
@@ -424,7 +527,7 @@ static int ehci_hub_control ( | |||
424 | default: | 527 | default: |
425 | goto error; | 528 | goto error; |
426 | } | 529 | } |
427 | readl (&ehci->regs->command); /* unblock posted write */ | 530 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */ |
428 | break; | 531 | break; |
429 | case GetHubDescriptor: | 532 | case GetHubDescriptor: |
430 | ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) | 533 | ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) |
@@ -440,7 +543,7 @@ static int ehci_hub_control ( | |||
440 | goto error; | 543 | goto error; |
441 | wIndex--; | 544 | wIndex--; |
442 | status = 0; | 545 | status = 0; |
443 | temp = readl (&ehci->regs->port_status [wIndex]); | 546 | temp = ehci_readl(ehci, status_reg); |
444 | 547 | ||
445 | // wPortChange bits | 548 | // wPortChange bits |
446 | if (temp & PORT_CSC) | 549 | if (temp & PORT_CSC) |
@@ -451,42 +554,55 @@ static int ehci_hub_control ( | |||
451 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; | 554 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; |
452 | 555 | ||
453 | /* whoever resumes must GetPortStatus to complete it!! */ | 556 | /* whoever resumes must GetPortStatus to complete it!! */ |
454 | if ((temp & PORT_RESUME) | 557 | if (temp & PORT_RESUME) { |
455 | && time_after (jiffies, | ||
456 | ehci->reset_done [wIndex])) { | ||
457 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; | ||
458 | ehci->reset_done [wIndex] = 0; | ||
459 | 558 | ||
460 | /* stop resume signaling */ | 559 | /* Remote Wakeup received? */ |
461 | temp = readl (&ehci->regs->port_status [wIndex]); | 560 | if (!ehci->reset_done[wIndex]) { |
462 | writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), | 561 | /* resume signaling for 20 msec */ |
463 | &ehci->regs->port_status [wIndex]); | 562 | ehci->reset_done[wIndex] = jiffies |
464 | retval = handshake ( | 563 | + msecs_to_jiffies(20); |
465 | &ehci->regs->port_status [wIndex], | 564 | /* check the port again */ |
466 | PORT_RESUME, 0, 2000 /* 2msec */); | 565 | mod_timer(&ehci_to_hcd(ehci)->rh_timer, |
467 | if (retval != 0) { | 566 | ehci->reset_done[wIndex]); |
468 | ehci_err (ehci, "port %d resume error %d\n", | 567 | } |
469 | wIndex + 1, retval); | 568 | |
470 | goto error; | 569 | /* resume completed? */ |
570 | else if (time_after_eq(jiffies, | ||
571 | ehci->reset_done[wIndex])) { | ||
572 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; | ||
573 | ehci->reset_done[wIndex] = 0; | ||
574 | |||
575 | /* stop resume signaling */ | ||
576 | temp = ehci_readl(ehci, status_reg); | ||
577 | ehci_writel(ehci, | ||
578 | temp & ~(PORT_RWC_BITS | PORT_RESUME), | ||
579 | status_reg); | ||
580 | retval = handshake(ehci, status_reg, | ||
581 | PORT_RESUME, 0, 2000 /* 2msec */); | ||
582 | if (retval != 0) { | ||
583 | ehci_err(ehci, | ||
584 | "port %d resume error %d\n", | ||
585 | wIndex + 1, retval); | ||
586 | goto error; | ||
587 | } | ||
588 | temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); | ||
471 | } | 589 | } |
472 | temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); | ||
473 | } | 590 | } |
474 | 591 | ||
475 | /* whoever resets must GetPortStatus to complete it!! */ | 592 | /* whoever resets must GetPortStatus to complete it!! */ |
476 | if ((temp & PORT_RESET) | 593 | if ((temp & PORT_RESET) |
477 | && time_after (jiffies, | 594 | && time_after_eq(jiffies, |
478 | ehci->reset_done [wIndex])) { | 595 | ehci->reset_done[wIndex])) { |
479 | status |= 1 << USB_PORT_FEAT_C_RESET; | 596 | status |= 1 << USB_PORT_FEAT_C_RESET; |
480 | ehci->reset_done [wIndex] = 0; | 597 | ehci->reset_done [wIndex] = 0; |
481 | 598 | ||
482 | /* force reset to complete */ | 599 | /* force reset to complete */ |
483 | writel (temp & ~(PORT_RWC_BITS | PORT_RESET), | 600 | ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), |
484 | &ehci->regs->port_status [wIndex]); | 601 | status_reg); |
485 | /* REVISIT: some hardware needs 550+ usec to clear | 602 | /* REVISIT: some hardware needs 550+ usec to clear |
486 | * this bit; seems too long to spin routinely... | 603 | * this bit; seems too long to spin routinely... |
487 | */ | 604 | */ |
488 | retval = handshake ( | 605 | retval = handshake(ehci, status_reg, |
489 | &ehci->regs->port_status [wIndex], | ||
490 | PORT_RESET, 0, 750); | 606 | PORT_RESET, 0, 750); |
491 | if (retval != 0) { | 607 | if (retval != 0) { |
492 | ehci_err (ehci, "port %d reset error %d\n", | 608 | ehci_err (ehci, "port %d reset error %d\n", |
@@ -495,28 +611,41 @@ static int ehci_hub_control ( | |||
495 | } | 611 | } |
496 | 612 | ||
497 | /* see what we found out */ | 613 | /* see what we found out */ |
498 | temp = check_reset_complete (ehci, wIndex, | 614 | temp = check_reset_complete (ehci, wIndex, status_reg, |
499 | readl (&ehci->regs->port_status [wIndex])); | 615 | ehci_readl(ehci, status_reg)); |
500 | } | 616 | } |
501 | 617 | ||
502 | // don't show wPortStatus if it's owned by a companion hc | 618 | /* transfer dedicated ports to the companion hc */ |
503 | if (!(temp & PORT_OWNER)) { | 619 | if ((temp & PORT_CONNECT) && |
504 | if (temp & PORT_CONNECT) { | 620 | test_bit(wIndex, &ehci->companion_ports)) { |
505 | status |= 1 << USB_PORT_FEAT_CONNECTION; | 621 | temp &= ~PORT_RWC_BITS; |
506 | // status may be from integrated TT | 622 | temp |= PORT_OWNER; |
507 | status |= ehci_port_speed(ehci, temp); | 623 | ehci_writel(ehci, temp, status_reg); |
508 | } | 624 | ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1); |
509 | if (temp & PORT_PE) | 625 | temp = ehci_readl(ehci, status_reg); |
510 | status |= 1 << USB_PORT_FEAT_ENABLE; | 626 | } |
511 | if (temp & (PORT_SUSPEND|PORT_RESUME)) | 627 | |
512 | status |= 1 << USB_PORT_FEAT_SUSPEND; | 628 | /* |
513 | if (temp & PORT_OC) | 629 | * Even if OWNER is set, there's no harm letting khubd |
514 | status |= 1 << USB_PORT_FEAT_OVER_CURRENT; | 630 | * see the wPortStatus values (they should all be 0 except |
515 | if (temp & PORT_RESET) | 631 | * for PORT_POWER anyway). |
516 | status |= 1 << USB_PORT_FEAT_RESET; | 632 | */ |
517 | if (temp & PORT_POWER) | 633 | |
518 | status |= 1 << USB_PORT_FEAT_POWER; | 634 | if (temp & PORT_CONNECT) { |
635 | status |= 1 << USB_PORT_FEAT_CONNECTION; | ||
636 | // status may be from integrated TT | ||
637 | status |= ehci_port_speed(ehci, temp); | ||
519 | } | 638 | } |
639 | if (temp & PORT_PE) | ||
640 | status |= 1 << USB_PORT_FEAT_ENABLE; | ||
641 | if (temp & (PORT_SUSPEND|PORT_RESUME)) | ||
642 | status |= 1 << USB_PORT_FEAT_SUSPEND; | ||
643 | if (temp & PORT_OC) | ||
644 | status |= 1 << USB_PORT_FEAT_OVER_CURRENT; | ||
645 | if (temp & PORT_RESET) | ||
646 | status |= 1 << USB_PORT_FEAT_RESET; | ||
647 | if (temp & PORT_POWER) | ||
648 | status |= 1 << USB_PORT_FEAT_POWER; | ||
520 | 649 | ||
521 | #ifndef EHCI_VERBOSE_DEBUG | 650 | #ifndef EHCI_VERBOSE_DEBUG |
522 | if (status & ~0xffff) /* only if wPortChange is interesting */ | 651 | if (status & ~0xffff) /* only if wPortChange is interesting */ |
@@ -541,7 +670,7 @@ static int ehci_hub_control ( | |||
541 | if (!wIndex || wIndex > ports) | 670 | if (!wIndex || wIndex > ports) |
542 | goto error; | 671 | goto error; |
543 | wIndex--; | 672 | wIndex--; |
544 | temp = readl (&ehci->regs->port_status [wIndex]); | 673 | temp = ehci_readl(ehci, status_reg); |
545 | if (temp & PORT_OWNER) | 674 | if (temp & PORT_OWNER) |
546 | break; | 675 | break; |
547 | 676 | ||
@@ -555,13 +684,12 @@ static int ehci_hub_control ( | |||
555 | goto error; | 684 | goto error; |
556 | if (device_may_wakeup(&hcd->self.root_hub->dev)) | 685 | if (device_may_wakeup(&hcd->self.root_hub->dev)) |
557 | temp |= PORT_WAKE_BITS; | 686 | temp |= PORT_WAKE_BITS; |
558 | writel (temp | PORT_SUSPEND, | 687 | ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); |
559 | &ehci->regs->port_status [wIndex]); | ||
560 | break; | 688 | break; |
561 | case USB_PORT_FEAT_POWER: | 689 | case USB_PORT_FEAT_POWER: |
562 | if (HCS_PPC (ehci->hcs_params)) | 690 | if (HCS_PPC (ehci->hcs_params)) |
563 | writel (temp | PORT_POWER, | 691 | ehci_writel(ehci, temp | PORT_POWER, |
564 | &ehci->regs->port_status [wIndex]); | 692 | status_reg); |
565 | break; | 693 | break; |
566 | case USB_PORT_FEAT_RESET: | 694 | case USB_PORT_FEAT_RESET: |
567 | if (temp & PORT_RESUME) | 695 | if (temp & PORT_RESUME) |
@@ -589,7 +717,7 @@ static int ehci_hub_control ( | |||
589 | ehci->reset_done [wIndex] = jiffies | 717 | ehci->reset_done [wIndex] = jiffies |
590 | + msecs_to_jiffies (50); | 718 | + msecs_to_jiffies (50); |
591 | } | 719 | } |
592 | writel (temp, &ehci->regs->port_status [wIndex]); | 720 | ehci_writel(ehci, temp, status_reg); |
593 | break; | 721 | break; |
594 | 722 | ||
595 | /* For downstream facing ports (these): one hub port is put | 723 | /* For downstream facing ports (these): one hub port is put |
@@ -604,13 +732,13 @@ static int ehci_hub_control ( | |||
604 | ehci_quiesce(ehci); | 732 | ehci_quiesce(ehci); |
605 | ehci_halt(ehci); | 733 | ehci_halt(ehci); |
606 | temp |= selector << 16; | 734 | temp |= selector << 16; |
607 | writel (temp, &ehci->regs->port_status [wIndex]); | 735 | ehci_writel(ehci, temp, status_reg); |
608 | break; | 736 | break; |
609 | 737 | ||
610 | default: | 738 | default: |
611 | goto error; | 739 | goto error; |
612 | } | 740 | } |
613 | readl (&ehci->regs->command); /* unblock posted writes */ | 741 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ |
614 | break; | 742 | break; |
615 | 743 | ||
616 | default: | 744 | default: |