From bb200f6eac6372839921be1fe87c1f5c292a7bd6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 3 Oct 2005 16:36:29 -0400 Subject: [PATCH] [PATCH] UHCI: unify BIOS handoff and driver reset code This patch (as574) updates the PCI BIOS usb-handoff code for UHCI controllers, making it work like the reset routines in uhci-hcd. This allows uhci-hcd to drop its own routines in favor of the new ones (code-sharing). Once the patch is merged we can turn the usb-handoff option on permanently, as far as UHCI is concerned. OHCI and EHCI may still have some issues. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 142 +++++++++++++++++++++++----------- drivers/usb/host/uhci-hcd.c | 76 +++--------------- 2 files changed, 106 insertions(+), 112 deletions(-) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 49f7381a6f..f7411ca488 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -9,6 +9,12 @@ */ #include +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif + #include #include #include @@ -46,13 +52,14 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, qui #define UHCI_USBLEGSUP 0xc0 /* legacy support */ #define UHCI_USBCMD 0 /* command register */ -#define UHCI_USBSTS 2 /* status register */ #define UHCI_USBINTR 4 /* interrupt register */ -#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ -#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ -#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ -#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ -#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ +#define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ +#define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ +#define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */ +#define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */ +#define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */ +#define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */ #define OHCI_CONTROL 0x04 #define OHCI_CMDSTATUS 0x08 @@ -84,57 +91,100 @@ static int __init usb_handoff_early(char *str) } __setup("usb-handoff", usb_handoff_early); -static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) { - unsigned long base = 0; - int wait_time, delta; - u16 val, sts; - int i; + /* 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. + */ + pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC); - for (i = 0; i < PCI_ROM_RESOURCE; i++) - if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { - base = pci_resource_start(pdev, i); - break; - } + /* Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD); + mb(); + udelay(5); + if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) + dev_warn(&pdev->dev, "HCRESET not completed yet!\n"); + + /* Just to be safe, disable interrupt requests and + * make sure the controller is stopped. + */ + outw(0, base + UHCI_USBINTR); + outw(0, base + UHCI_USBCMD); +} +EXPORT_SYMBOL_GPL(uhci_reset_hc); - if (!base) - return; +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) +{ + u16 legsup; + unsigned int cmd, intr; /* - * stop controller + * When restarting a suspended controller, we expect all the + * settings to be the same as we left them: + * + * 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. */ - sts = inw(base + UHCI_USBSTS); - val = inw(base + UHCI_USBCMD); - val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); - outw(val, base + UHCI_USBCMD); + pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup); + if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) { + dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n", + __FUNCTION__, legsup); + goto reset_needed; + } - /* - * wait while it stops if it was running - */ - if ((sts & UHCI_USBSTS_HALTED) == 0) - { - wait_time = 1000; - delta = 100; + cmd = inw(base + UHCI_USBCMD); + if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) || + !(cmd & UHCI_USBCMD_EGSM)) { + dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n", + __FUNCTION__, cmd); + goto reset_needed; + } - do { - outw(0x1f, base + UHCI_USBSTS); - udelay(delta); - wait_time -= delta; - val = inw(base + UHCI_USBSTS); - if (val & UHCI_USBSTS_HALTED) - break; - } while (wait_time > 0); + intr = inw(base + UHCI_USBINTR); + if (intr & (~UHCI_USBINTR_RESUME)) { + dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n", + __FUNCTION__, intr); + goto reset_needed; } + return 0; - /* - * disable interrupts & legacy support - */ - outw(0, base + UHCI_USBINTR); - outw(0x1f, base + UHCI_USBSTS); - pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); - if (val & 0xbf) - pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); +reset_needed: + dev_dbg(&pdev->dev, "Performing full reset\n"); + uhci_reset_hc(pdev, base); + return 1; +} +EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); + +static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) +{ + unsigned long base = 0; + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { + base = pci_resource_start(pdev, i); + break; + } + if (base) + uhci_check_and_reset_hc(pdev, base); } static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 34c9dbc6a1..6df555e3d9 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -101,37 +101,16 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); #include "uhci-q.c" #include "uhci-hub.c" +extern void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); +extern int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); + /* - * Make sure the controller is completely inactive, unable to - * generate interrupts or do DMA. + * Finish up a host controller reset and update the recorded state. */ -static void reset_hc(struct uhci_hcd *uhci) +static void finish_reset(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. - */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, - USBLEGSUP_RWC); - - /* Reset the HC - this will force us to get a - * new notification of any already connected - * ports due to the virtual disconnect that it - * implies. - */ - outw(USBCMD_HCRESET, uhci->io_addr + USBCMD); - mb(); - udelay(5); - if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET) - dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); - - /* Just to be safe, disable interrupt requests and - * make sure the controller is stopped. - */ - 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. @@ -153,7 +132,8 @@ static void reset_hc(struct uhci_hcd *uhci) */ static void hc_died(struct uhci_hcd *uhci) { - reset_hc(uhci); + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + finish_reset(uhci); uhci->hc_inaccessible = 1; } @@ -163,44 +143,8 @@ static void hc_died(struct uhci_hcd *uhci) */ static void check_and_reset_hc(struct uhci_hcd *uhci) { - u16 legsup; - unsigned int cmd, intr; - - /* - * When restarting a suspended controller, we expect all the - * settings to be the same as we left them: - * - * 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 | USBLEGSUP_RWC)) { - dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n", - __FUNCTION__, legsup); - goto reset_needed; - } - - cmd = inw(uhci->io_addr + USBCMD); - if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { - dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", - __FUNCTION__, cmd); - goto reset_needed; - } - - intr = inw(uhci->io_addr + USBINTR); - if (intr & (~USBINTR_RESUME)) { - dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", - __FUNCTION__, intr); - goto reset_needed; - } - return; - -reset_needed: - dev_dbg(uhci_dev(uhci), "Performing full reset\n"); - reset_hc(uhci); + if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) + finish_reset(uhci); } /* @@ -714,7 +658,7 @@ static void uhci_stop(struct usb_hcd *hcd) spin_lock_irq(&uhci->lock); if (!uhci->hc_inaccessible) - reset_hc(uhci); + hc_died(uhci); uhci_scan_schedule(uhci, NULL); spin_unlock_irq(&uhci->lock); -- 2.39.5