diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-10-10 10:54:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-10-17 17:46:31 -0400 |
commit | b62df4516981745d4b5de01ceec1d65a9174a524 (patch) | |
tree | b7eae113a1fc6b860763abb91ca49629552cbd83 | |
parent | 96a518928e1fd00a6d0eb344f420ea82aeec8ab9 (diff) |
UHCI: workaround for Asus motherboard
This patch (as798) adds a workaround to uhci-hcd. At least one Asus
motherboard is wired in such a way that any device attached to a
suspended UHCI controller will prevent the system from entering
suspend-to-RAM by immediately waking it up. The only way around the
problem is to turn the controller off instead of suspending it.
This fixes Bugzilla #6193.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 44 |
1 files changed, 39 insertions, 5 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 45ee6920a850..226bf3de8edd 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/dma-mapping.h> | 40 | #include <linux/dma-mapping.h> |
41 | #include <linux/usb.h> | 41 | #include <linux/usb.h> |
42 | #include <linux/bitops.h> | 42 | #include <linux/bitops.h> |
43 | #include <linux/dmi.h> | ||
43 | 44 | ||
44 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
45 | #include <asm/io.h> | 46 | #include <asm/io.h> |
@@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) | |||
196 | return 0; | 197 | return 0; |
197 | } | 198 | } |
198 | 199 | ||
200 | static int remote_wakeup_is_broken(struct uhci_hcd *uhci) | ||
201 | { | ||
202 | static struct dmi_system_id broken_wakeup_table[] = { | ||
203 | { | ||
204 | .ident = "Asus A7V8X", | ||
205 | .matches = { | ||
206 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"), | ||
207 | DMI_MATCH(DMI_BOARD_NAME, "A7V8X"), | ||
208 | DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"), | ||
209 | } | ||
210 | }, | ||
211 | { } | ||
212 | }; | ||
213 | int port; | ||
214 | |||
215 | /* One of Asus's motherboards has a bug which causes it to | ||
216 | * wake up immediately from suspend-to-RAM if any of the ports | ||
217 | * are connected. In such cases we will not set EGSM. | ||
218 | */ | ||
219 | if (dmi_check_system(broken_wakeup_table)) { | ||
220 | for (port = 0; port < uhci->rh_numports; ++port) { | ||
221 | if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & | ||
222 | USBPORTSC_CCS) | ||
223 | return 1; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
199 | static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) | 230 | static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) |
200 | __releases(uhci->lock) | 231 | __releases(uhci->lock) |
201 | __acquires(uhci->lock) | 232 | __acquires(uhci->lock) |
202 | { | 233 | { |
203 | int auto_stop; | 234 | int auto_stop; |
204 | int int_enable; | 235 | int int_enable, egsm_enable; |
205 | 236 | ||
206 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); | 237 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); |
207 | dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev, | 238 | dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev, |
@@ -217,15 +248,18 @@ __acquires(uhci->lock) | |||
217 | } | 248 | } |
218 | 249 | ||
219 | /* Enable resume-detect interrupts if they work. | 250 | /* Enable resume-detect interrupts if they work. |
220 | * Then enter Global Suspend mode, still configured. | 251 | * Then enter Global Suspend mode if _it_ works, still configured. |
221 | */ | 252 | */ |
253 | egsm_enable = USBCMD_EGSM; | ||
222 | uhci->working_RD = 1; | 254 | uhci->working_RD = 1; |
223 | int_enable = USBINTR_RESUME; | 255 | int_enable = USBINTR_RESUME; |
224 | if (resume_detect_interrupts_are_broken(uhci)) { | 256 | if (remote_wakeup_is_broken(uhci)) |
257 | egsm_enable = 0; | ||
258 | if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable) | ||
225 | uhci->working_RD = int_enable = 0; | 259 | uhci->working_RD = int_enable = 0; |
226 | } | 260 | |
227 | outw(int_enable, uhci->io_addr + USBINTR); | 261 | outw(int_enable, uhci->io_addr + USBINTR); |
228 | outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); | 262 | outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); |
229 | mb(); | 263 | mb(); |
230 | udelay(5); | 264 | udelay(5); |
231 | 265 | ||