diff options
author | Geoff Levand <geoff@infradead.org> | 2011-11-30 19:40:57 -0500 |
---|---|---|
committer | Geoff Levand <geoff@infradead.org> | 2011-12-08 12:38:53 -0500 |
commit | df7c1ca229ebffe14a6fb3f13d16b1dd2a1731cb (patch) | |
tree | eb99177373815ded62106db7423a022520402fa7 /drivers/usb/host | |
parent | 9187bef2fa395e85d5e22c4792b553d98410ccd6 (diff) |
usb: Fix PS3 EHCI suspend
The EHCI USB controller of the Cell Super Companion Chip used in the PS3
will stop the root hub after all root hub ports are suspended. When in
this condition the ehci-hcd handshake routine will return -ETIMEDOUT and
the USB runtime suspend sequence will fail. The STS_HLT bit will not be
set, so inspection of the frame index is used to test for the condition.
Add a new routine handshake_for_broken_root_hub() that is called after
an unsuccessful -ETIMEDOUT handshake. On PS3 handshake_for_broken_root_hub()
will test for the condition, and if found will return success to allow the
USB suspend to complete. For all other platforms
handshake_for_broken_root_hub() will return -ETIMEDOUT
Signed-off-by: Geoff Levand <geoff@infradead.org>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 46dccbf85c1a..e0ca9955880b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -48,6 +48,10 @@ | |||
48 | #include <asm/system.h> | 48 | #include <asm/system.h> |
49 | #include <asm/unaligned.h> | 49 | #include <asm/unaligned.h> |
50 | 50 | ||
51 | #if defined(CONFIG_PPC_PS3) | ||
52 | #include <asm/firmware.h> | ||
53 | #endif | ||
54 | |||
51 | /*-------------------------------------------------------------------------*/ | 55 | /*-------------------------------------------------------------------------*/ |
52 | 56 | ||
53 | /* | 57 | /* |
@@ -230,12 +234,58 @@ static int ehci_halt (struct ehci_hcd *ehci) | |||
230 | STS_HALT, STS_HALT, 16 * 125); | 234 | STS_HALT, STS_HALT, 16 * 125); |
231 | } | 235 | } |
232 | 236 | ||
237 | #if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3) | ||
238 | |||
239 | /* | ||
240 | * The EHCI controller of the Cell Super Companion Chip used in the | ||
241 | * PS3 will stop the root hub after all root hub ports are suspended. | ||
242 | * When in this condition handshake will return -ETIMEDOUT. The | ||
243 | * STS_HLT bit will not be set, so inspection of the frame index is | ||
244 | * used here to test for the condition. If the condition is found | ||
245 | * return success to allow the USB suspend to complete. | ||
246 | */ | ||
247 | |||
248 | static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, | ||
249 | void __iomem *ptr, u32 mask, u32 done, | ||
250 | int usec) | ||
251 | { | ||
252 | unsigned int old_index; | ||
253 | int error; | ||
254 | |||
255 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
256 | return -ETIMEDOUT; | ||
257 | |||
258 | old_index = ehci_read_frame_index(ehci); | ||
259 | |||
260 | error = handshake(ehci, ptr, mask, done, usec); | ||
261 | |||
262 | if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index) | ||
263 | return 0; | ||
264 | |||
265 | return error; | ||
266 | } | ||
267 | |||
268 | #else | ||
269 | |||
270 | static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, | ||
271 | void __iomem *ptr, u32 mask, u32 done, | ||
272 | int usec) | ||
273 | { | ||
274 | return -ETIMEDOUT; | ||
275 | } | ||
276 | |||
277 | #endif | ||
278 | |||
233 | static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, | 279 | static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, |
234 | u32 mask, u32 done, int usec) | 280 | u32 mask, u32 done, int usec) |
235 | { | 281 | { |
236 | int error; | 282 | int error; |
237 | 283 | ||
238 | error = handshake(ehci, ptr, mask, done, usec); | 284 | error = handshake(ehci, ptr, mask, done, usec); |
285 | if (error == -ETIMEDOUT) | ||
286 | error = handshake_for_broken_root_hub(ehci, ptr, mask, done, | ||
287 | usec); | ||
288 | |||
239 | if (error) { | 289 | if (error) { |
240 | ehci_halt(ehci); | 290 | ehci_halt(ehci); |
241 | ehci->rh_state = EHCI_RH_HALTED; | 291 | ehci->rh_state = EHCI_RH_HALTED; |