aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-04-22 14:39:12 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-06-27 14:43:44 -0700
commitc074b416b94c0aa4a371f24bf6cc13d8cf1fab59 (patch)
tree51cfe6ca9293b9484108ed757ec3c43eb5328474 /drivers/usb/host
parent6c1b445c226dd82d0961725dec8051b95003723a (diff)
downloadkernel_samsung_smdk4412-c074b416b94c0aa4a371f24bf6cc13d8cf1fab59.zip
kernel_samsung_smdk4412-c074b416b94c0aa4a371f24bf6cc13d8cf1fab59.tar.gz
kernel_samsung_smdk4412-c074b416b94c0aa4a371f24bf6cc13d8cf1fab59.tar.bz2
[PATCH] USB UHCI: improved reset handling
This patch improves the strategy uhci-hcd uses for performing controller resets and checking whether they are needed. The HCRESET command doesn't affect the Suspend, Resume, or Reset bits in the port status & control registers, so the driver must clear them by itself. This means the code to figure out how many ports there are has to be moved to an earlier spot in the driver. The R/WC bits in the USBLEGSUP register can be set by the hardware even in the absence of BIOS meddling with legacy support features. Hence it's not a good idea to check them while trying to determine whether the BIOS has altered the controller's state. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/uhci-hcd.c69
1 files changed, 40 insertions, 29 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 25a718e..cec070f 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -112,6 +112,8 @@ static inline void restart_timer(struct uhci_hcd *uhci)
*/
static void reset_hc(struct uhci_hcd *uhci)
{
+ int port;
+
/* Turn off PIRQ enable and SMI enable. (This also turns off the
* BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
*/
@@ -135,6 +137,13 @@ static void reset_hc(struct uhci_hcd *uhci)
outw(0, uhci->io_addr + USBINTR);
outw(0, uhci->io_addr + USBCMD);
+ /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
+ * bits in the port status and control registers.
+ * We have to clear them by hand.
+ */
+ for (port = 0; port < uhci->rh_numports; ++port)
+ outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
+
uhci->port_c_suspend = uhci->suspended_ports =
uhci->resuming_ports = 0;
uhci->rh_state = UHCI_RH_RESET;
@@ -166,14 +175,14 @@ static void check_and_reset_hc(struct uhci_hcd *uhci)
* When restarting a suspended controller, we expect all the
* settings to be the same as we left them:
*
- * PIRQ and SMI disabled, no R/WC bits set in USBLEGSUP;
+ * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
* Controller is stopped and configured with EGSM set;
* No interrupts enabled except possibly Resume Detect.
*
* If any of these conditions are violated we do a complete reset.
*/
pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
- if (legsup & ~USBLEGSUP_RO) {
+ if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
__FUNCTION__, legsup);
goto reset_needed;
@@ -478,9 +487,37 @@ static void release_uhci(struct uhci_hcd *uhci)
static int uhci_reset(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned io_size = (unsigned) hcd->rsrc_len;
+ int port;
uhci->io_addr = (unsigned long) hcd->rsrc_start;
+ /* The UHCI spec says devices must have 2 ports, and goes on to say
+ * they may have more but gives no way to determine how many there
+ * are. However, according to the UHCI spec, Bit 7 of the port
+ * status and control register is always set to 1. So we try to
+ * use this to our advantage.
+ */
+ for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
+ unsigned int portstatus;
+
+ portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
+ if (!(portstatus & 0x0080))
+ break;
+ }
+ if (debug)
+ dev_info(uhci_dev(uhci), "detected %d ports\n", port);
+
+ /* Anything less than 2 or greater than 7 is weird,
+ * so we'll ignore it.
+ */
+ if (port < 2 || port > UHCI_RH_MAXCHILD) {
+ dev_info(uhci_dev(uhci), "port count misdetected? "
+ "forcing to 2 ports\n");
+ port = 2;
+ }
+ uhci->rh_numports = port;
+
/* Kick BIOS off this hardware and reset if the controller
* isn't already safely quiescent.
*/
@@ -508,13 +545,11 @@ static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
- int i, port;
- unsigned io_size;
+ int i;
dma_addr_t dma_handle;
struct usb_device *udev;
struct dentry *dentry;
- io_size = (unsigned) hcd->rsrc_len;
hcd->uses_new_polling = 1;
if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
hcd->can_wakeup = 1; /* Assume it supports PME# */
@@ -578,30 +613,6 @@ static int uhci_start(struct usb_hcd *hcd)
/* Initialize the root hub */
- /* UHCI specs says devices must have 2 ports, but goes on to say */
- /* they may have more but give no way to determine how many they */
- /* have. However, according to the UHCI spec, Bit 7 is always set */
- /* to 1. So we try to use this to our advantage */
- for (port = 0; port < (io_size - 0x10) / 2; port++) {
- unsigned int portstatus;
-
- portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
- if (!(portstatus & 0x0080))
- break;
- }
- if (debug)
- dev_info(uhci_dev(uhci), "detected %d ports\n", port);
-
- /* This is experimental so anything less than 2 or greater than 8 is */
- /* something weird and we'll ignore it */
- if (port < 2 || port > UHCI_RH_MAXCHILD) {
- dev_info(uhci_dev(uhci), "port count misdetected? "
- "forcing to 2 ports\n");
- port = 2;
- }
-
- uhci->rh_numports = port;
-
udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n");