From f5946f8220a866dcdb8edc6abe23c1443e252425 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 9 Apr 2005 17:26:00 -0400 Subject: [PATCH] USB UHCI: Minor improvements This patch makes a few small improvements in the UHCI driver. Some code is moved between different source files and a more useful pointer is passed to a callback routine. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb/host/uhci-hub.c') diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 4c45ba8390f..ddb19cbf4b7 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -33,6 +33,22 @@ static __u8 root_hub_hub_des[] = /* status change bits: nonzero writes will clear */ #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) +/* A port that either is connected or has a changed-bit set will prevent + * us from AUTO_STOPPING. + */ +static int any_ports_active(struct uhci_hcd *uhci) +{ + int port; + + for (port = 0; port < uhci->rh_numports; ++port) { + if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & + (USBPORTSC_CCS | RWC_BITS)) || + test_bit(port, &uhci->port_c_suspend)) + return 1; + } + return 0; +} + static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); -- cgit v1.2.3 From c8f4fe4358c5e0a79b4bd47b814d19f1d1d06f21 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 9 Apr 2005 17:27:32 -0400 Subject: [PATCH] USB UHCI: Add root hub states This patch starts making some serious changes to the UHCI driver. There's a set of private states for the root hub, and the internal routines for suspending and resuming work completely differently, with transitions based on the new states. Now the driver distinguishes between a privately auto-stopped state and a publicly suspended state, and it will properly suspend controllers with broken resume-detect interrupts instead of resetting them. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host/uhci-hub.c') diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index ddb19cbf4b7..fc34fee2ab0 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -60,7 +60,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } - if (*buf && uhci->state == UHCI_SUSPENDED) + if (*buf && uhci->is_stopped) uhci->resume_detect = 1; return !!*buf; } -- cgit v1.2.3 From a8bed8b6be75bc5a46aa599ab360d5f1db291c8f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 9 Apr 2005 17:29:00 -0400 Subject: [PATCH] USB UHCI: Add root-hub suspend/resume support This patch implements (finally!) separate suspend and resume routines for the root hub and the controller in the UHCI driver. It also changes the sequence used to reset the controller during initial probing, so as to preserve the existing state during a Resume-From-Disk. (This new sequence is what should be used in the PCI Quirks code for early USB handoffs, incidentally.) Lastly it adds a notion of the controller being "inaccessible" while in a PCI low-power state, when normal I/O operations shouldn't be allowed. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb/host/uhci-hub.c') diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index fc34fee2ab0..13652de5220 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -54,6 +54,9 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) struct uhci_hcd *uhci = hcd_to_uhci(hcd); int port; + if (uhci->hc_inaccessible) + return 0; + *buf = 0; for (port = 0; port < uhci->rh_numports; ++port) { if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || @@ -150,6 +153,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wPortChange, wPortStatus; unsigned long flags; + if (uhci->hc_inaccessible) + return -ETIMEDOUT; + spin_lock_irqsave(&uhci->lock, flags); switch (typeReq) { -- cgit v1.2.3 From 6c1b445c226dd82d0961725dec8051b95003723a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Apr 2005 16:04:58 -0400 Subject: [PATCH] USB UHCI: Use root-hub IRQs while suspended This patch, which has as478b as a prerequisite, enables the uhci-hcd driver to take advantage of root-hub IRQs rather than polling during the time it is suspended. (Unfortunately the hardware doesn't support port-change interrupts while the controller is running.) It also turns off the driver's private timer while the controller is suspended, as it isn't needed then. The combined elimination of polling interrupts and timer interrupts ought to be enough to allow some systems to save a noticeable amount of power while they are otherwise idle. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 67 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) (limited to 'drivers/usb/host/uhci-hub.c') diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 13652de5220..4eace2b19dd 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -49,22 +49,16 @@ static int any_ports_active(struct uhci_hcd *uhci) return 0; } -static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); int port; - if (uhci->hc_inaccessible) - return 0; - *buf = 0; for (port = 0; port < uhci->rh_numports; ++port) { if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } - if (*buf && uhci->is_stopped) - uhci->resume_detect = 1; return !!*buf; } @@ -134,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci) set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(20); + + /* Make sure we see the port again + * after the resuming period is over. */ + mod_timer(&uhci_to_hcd(uhci)->rh_timer, + uhci->ports_timeout); } else if (time_after_eq(jiffies, uhci->ports_timeout)) { uhci_finish_suspend(uhci, port, port_addr); @@ -142,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci) } } +static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + int status; + + spin_lock_irqsave(&uhci->lock, flags); + if (uhci->hc_inaccessible) { + status = 0; + goto done; + } + + uhci_check_ports(uhci); + status = get_hub_status_data(uhci, buf); + + switch (uhci->rh_state) { + case UHCI_RH_SUSPENDING: + case UHCI_RH_SUSPENDED: + /* if port change, ask to be resumed */ + if (status) + usb_hcd_resume_root_hub(hcd); + break; + + case UHCI_RH_AUTO_STOPPED: + /* if port change, auto start */ + if (status) + wakeup_rh(uhci); + break; + + case UHCI_RH_RUNNING: + /* are any devices attached? */ + if (!any_ports_active(uhci)) { + uhci->rh_state = UHCI_RH_RUNNING_NODEVS; + uhci->auto_stop_time = jiffies + HZ; + } + break; + + case UHCI_RH_RUNNING_NODEVS: + /* auto-stop if nothing connected for 1 second */ + if (any_ports_active(uhci)) + uhci->rh_state = UHCI_RH_RUNNING; + else if (time_after_eq(jiffies, uhci->auto_stop_time)) + suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); + break; + + default: + break; + } + +done: + spin_unlock_irqrestore(&uhci->lock, flags); + return status; +} + /* size of returned buffer is part of USB spec */ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) -- cgit v1.2.3