ChangeSet 1.1623, 2003/08/01 10:08:08-07:00, david-b@pacbell.net [PATCH] USB: hcd initialization fix This cleans up HCD initialization by adding an explicit reset step, putting the device into a known state before resources are allocated. This step is implemented for EHCI, since some BIOS firmware seems to act quirky there, but nothing else yet. (OHCI would be just easy too.) drivers/usb/core/hcd-pci.c | 21 ++++++++++++++------- drivers/usb/core/hcd.h | 1 + drivers/usb/host/ehci-hcd.c | 31 ++++++++++++++++++------------- 3 files changed, 33 insertions(+), 20 deletions(-) diff -Nru a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c --- a/drivers/usb/core/hcd-pci.c Fri Aug 1 10:53:34 2003 +++ b/drivers/usb/core/hcd-pci.c Fri Aug 1 10:53:34 2003 @@ -122,10 +122,9 @@ base = (void *) resource; } - // driver->start(), later on, will transfer device from + // driver->reset(), later on, will transfer device from // control by SMM/BIOS to control by Linux (if needed) - pci_set_master (dev); hcd = driver->hcd_alloc (); if (hcd == NULL){ dbg ("hcd alloc fail"); @@ -140,6 +139,9 @@ return retval; } } + hcd->regs = base; + hcd->region = region; + pci_set_drvdata (dev, hcd); hcd->driver = driver; hcd->description = driver->description; @@ -157,22 +159,27 @@ dev_info (hcd->controller, "%s\n", hcd->product_desc); + /* till now HC has been in an indeterminate state ... */ + if (driver->reset && (retval = driver->reset (hcd)) < 0) { + dev_err (hcd->controller, "can't reset\n"); + goto clean_3; + } + + pci_set_master (dev); #ifndef __sparc__ sprintf (buf, "%d", dev->irq); #else bufp = __irq_itoa(dev->irq); #endif - if (request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd) - != 0) { + retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, + hcd->description, hcd); + if (retval != 0) { dev_err (hcd->controller, "request interrupt %s failed\n", bufp); - retval = -EBUSY; goto clean_3; } hcd->irq = dev->irq; - hcd->regs = base; - hcd->region = region; dev_info (hcd->controller, "irq %s, %s %p\n", bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", base); diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Fri Aug 1 10:53:34 2003 +++ b/drivers/usb/core/hcd.h Fri Aug 1 10:53:34 2003 @@ -173,6 +173,7 @@ #define HCD_USB2 0x0020 /* USB 2.0 */ /* called to init HCD and root hub */ + int (*reset) (struct usb_hcd *hcd); int (*start) (struct usb_hcd *hcd); /* called after all devices were suspended */ diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Fri Aug 1 10:53:34 2003 +++ b/drivers/usb/host/ehci-hcd.c Fri Aug 1 10:53:34 2003 @@ -318,28 +318,21 @@ /* called by khubd or root hub init threads */ -static int ehci_start (struct usb_hcd *hcd) +static int ehci_hc_reset (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; - struct usb_device *udev; - struct usb_bus *bus; - int retval; - u32 hcc_params; - u8 tempbyte; spin_lock_init (&ehci->lock); ehci->caps = (struct ehci_caps *) hcd->regs; ehci->regs = (struct ehci_regs *) (hcd->regs + readb (&ehci->caps->length)); - dbg_hcs_params (ehci, "ehci_start"); - dbg_hcc_params (ehci, "ehci_start"); - - hcc_params = readl (&ehci->caps->hcc_params); + dbg_hcs_params (ehci, "reset"); + dbg_hcc_params (ehci, "reset"); /* EHCI 0.96 and later may have "extended capabilities" */ - temp = HCC_EXT_CAPS (hcc_params); + temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); while (temp) { u32 cap; @@ -364,8 +357,18 @@ ehci->hcs_params = readl (&ehci->caps->hcs_params); /* force HC to halt state */ - if ((retval = ehci_halt (ehci)) != 0) - return retval; + return ehci_halt (ehci); +} + +static int ehci_start (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 temp; + struct usb_device *udev; + struct usb_bus *bus; + int retval; + u32 hcc_params; + u8 tempbyte; /* * hw default: 1K periodic list heads, one per frame. @@ -376,6 +379,7 @@ return retval; /* controllers may cache some of the periodic schedule ... */ + hcc_params = readl (&ehci->caps->hcc_params); if (HCC_ISOC_CACHE (hcc_params)) // full frame cache ehci->i_thresh = 8; else // N microframes cached @@ -937,6 +941,7 @@ /* * basic lifecycle operations */ + .reset = ehci_hc_reset, .start = ehci_start, #ifdef CONFIG_PM .suspend = ehci_suspend,