From stern@rowland.harvard.edu Tue Oct 10 07:54:28 2006 Date: Tue, 10 Oct 2006 10:54:00 -0400 (EDT) From: Alan Stern To: Greg KH cc: newsuser@famdijkstra.org, USB development list Subject: UHCI: workaround for Asus motherboard Message-ID: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) --- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.c +++ gregkh-2.6/drivers/usb/host/uhci-hcd.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_ return 0; } +static int remote_wakeup_is_broken(struct uhci_hcd *uhci) +{ + static struct dmi_system_id broken_wakeup_table[] = { + { + .ident = "Asus A7V8X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"), + DMI_MATCH(DMI_BOARD_NAME, "A7V8X"), + DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"), + } + }, + { } + }; + int port; + + /* One of Asus's motherboards has a bug which causes it to + * wake up immediately from suspend-to-RAM if any of the ports + * are connected. In such cases we will not set EGSM. + */ + if (dmi_check_system(broken_wakeup_table)) { + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_CCS) + return 1; + } + } + + return 0; +} + static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) __releases(uhci->lock) __acquires(uhci->lock) { int auto_stop; - int int_enable; + int int_enable, egsm_enable; auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev, @@ -217,15 +248,18 @@ __acquires(uhci->lock) } /* Enable resume-detect interrupts if they work. - * Then enter Global Suspend mode, still configured. + * Then enter Global Suspend mode if _it_ works, still configured. */ + egsm_enable = USBCMD_EGSM; uhci->working_RD = 1; int_enable = USBINTR_RESUME; - if (resume_detect_interrupts_are_broken(uhci)) { + if (remote_wakeup_is_broken(uhci)) + egsm_enable = 0; + if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable) uhci->working_RD = int_enable = 0; - } + outw(int_enable, uhci->io_addr + USBINTR); - outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); + outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); mb(); udelay(5);