| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * This file contains code to reset and initialize USB host controllers. | 
|  | 3 | * Some of it includes work-arounds for PCI hardware and BIOS quirks. | 
|  | 4 | * It may need to run early during booting -- before USB would normally | 
|  | 5 | * initialize -- to ensure that Linux doesn't use any legacy modes. | 
|  | 6 | * | 
|  | 7 | *  Copyright (c) 1999 Martin Mares <mj@ucw.cz> | 
|  | 8 | *  (and others) | 
|  | 9 | */ | 
|  | 10 |  | 
|  | 11 | #include <linux/config.h> | 
|  | 12 | #include <linux/types.h> | 
|  | 13 | #include <linux/kernel.h> | 
|  | 14 | #include <linux/pci.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/delay.h> | 
|  | 17 | #include <linux/acpi.h> | 
|  | 18 |  | 
|  | 19 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 20 | #define UHCI_USBLEGSUP		0xc0		/* legacy support */ | 
|  | 21 | #define UHCI_USBCMD		0		/* command register */ | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 22 | #define UHCI_USBINTR		4		/* interrupt register */ | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 23 | #define UHCI_USBLEGSUP_RWC	0x8f00		/* the R/WC bits */ | 
|  | 24 | #define UHCI_USBLEGSUP_RO	0x5040		/* R/O and reserved bits */ | 
|  | 25 | #define UHCI_USBCMD_RUN		0x0001		/* RUN/STOP bit */ | 
|  | 26 | #define UHCI_USBCMD_HCRESET	0x0002		/* Host Controller reset */ | 
|  | 27 | #define UHCI_USBCMD_EGSM	0x0008		/* Global Suspend Mode */ | 
|  | 28 | #define UHCI_USBCMD_CONFIGURE	0x0040		/* Config Flag */ | 
|  | 29 | #define UHCI_USBINTR_RESUME	0x0002		/* Resume interrupt enable */ | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 30 |  | 
|  | 31 | #define OHCI_CONTROL		0x04 | 
|  | 32 | #define OHCI_CMDSTATUS		0x08 | 
|  | 33 | #define OHCI_INTRSTATUS		0x0c | 
|  | 34 | #define OHCI_INTRENABLE		0x10 | 
|  | 35 | #define OHCI_INTRDISABLE	0x14 | 
|  | 36 | #define OHCI_OCR		(1 << 3)	/* ownership change request */ | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 37 | #define OHCI_CTRL_RWC		(1 << 9)	/* remote wakeup connected */ | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 38 | #define OHCI_CTRL_IR		(1 << 8)	/* interrupt routing */ | 
|  | 39 | #define OHCI_INTR_OC		(1 << 30)	/* ownership change */ | 
|  | 40 |  | 
|  | 41 | #define EHCI_HCC_PARAMS		0x08		/* extended capabilities */ | 
|  | 42 | #define EHCI_USBCMD		0		/* command register */ | 
|  | 43 | #define EHCI_USBCMD_RUN		(1 << 0)	/* RUN/STOP bit */ | 
|  | 44 | #define EHCI_USBSTS		4		/* status register */ | 
|  | 45 | #define EHCI_USBSTS_HALTED	(1 << 12)	/* HCHalted bit */ | 
|  | 46 | #define EHCI_USBINTR		8		/* interrupt register */ | 
|  | 47 | #define EHCI_USBLEGSUP		0		/* legacy support register */ | 
|  | 48 | #define EHCI_USBLEGSUP_BIOS	(1 << 16)	/* BIOS semaphore */ | 
|  | 49 | #define EHCI_USBLEGSUP_OS	(1 << 24)	/* OS semaphore */ | 
|  | 50 | #define EHCI_USBLEGCTLSTS	4		/* legacy control/status */ | 
|  | 51 | #define EHCI_USBLEGCTLSTS_SOOE	(1 << 13)	/* SMI on ownership change */ | 
|  | 52 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 53 |  | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 54 | /* | 
|  | 55 | * Make sure the controller is completely inactive, unable to | 
|  | 56 | * generate interrupts or do DMA. | 
|  | 57 | */ | 
|  | 58 | void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) | 
|  | 59 | { | 
|  | 60 | /* Turn off PIRQ enable and SMI enable.  (This also turns off the | 
|  | 61 | * BIOS's USB Legacy Support.)  Turn off all the R/WC bits too. | 
|  | 62 | */ | 
|  | 63 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC); | 
|  | 64 |  | 
|  | 65 | /* Reset the HC - this will force us to get a | 
|  | 66 | * new notification of any already connected | 
|  | 67 | * ports due to the virtual disconnect that it | 
|  | 68 | * implies. | 
|  | 69 | */ | 
|  | 70 | outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD); | 
|  | 71 | mb(); | 
|  | 72 | udelay(5); | 
|  | 73 | if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) | 
|  | 74 | dev_warn(&pdev->dev, "HCRESET not completed yet!\n"); | 
|  | 75 |  | 
|  | 76 | /* Just to be safe, disable interrupt requests and | 
|  | 77 | * make sure the controller is stopped. | 
|  | 78 | */ | 
|  | 79 | outw(0, base + UHCI_USBINTR); | 
|  | 80 | outw(0, base + UHCI_USBCMD); | 
|  | 81 | } | 
|  | 82 | EXPORT_SYMBOL_GPL(uhci_reset_hc); | 
|  | 83 |  | 
|  | 84 | /* | 
|  | 85 | * Initialize a controller that was newly discovered or has just been | 
|  | 86 | * resumed.  In either case we can't be sure of its previous state. | 
|  | 87 | * | 
|  | 88 | * Returns: 1 if the controller was reset, 0 otherwise. | 
|  | 89 | */ | 
|  | 90 | int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) | 
|  | 91 | { | 
|  | 92 | u16 legsup; | 
|  | 93 | unsigned int cmd, intr; | 
|  | 94 |  | 
|  | 95 | /* | 
|  | 96 | * When restarting a suspended controller, we expect all the | 
|  | 97 | * settings to be the same as we left them: | 
|  | 98 | * | 
|  | 99 | *	PIRQ and SMI disabled, no R/W bits set in USBLEGSUP; | 
|  | 100 | *	Controller is stopped and configured with EGSM set; | 
|  | 101 | *	No interrupts enabled except possibly Resume Detect. | 
|  | 102 | * | 
|  | 103 | * If any of these conditions are violated we do a complete reset. | 
|  | 104 | */ | 
|  | 105 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup); | 
|  | 106 | if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) { | 
|  | 107 | dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n", | 
|  | 108 | __FUNCTION__, legsup); | 
|  | 109 | goto reset_needed; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | cmd = inw(base + UHCI_USBCMD); | 
|  | 113 | if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) || | 
|  | 114 | !(cmd & UHCI_USBCMD_EGSM)) { | 
|  | 115 | dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n", | 
|  | 116 | __FUNCTION__, cmd); | 
|  | 117 | goto reset_needed; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | intr = inw(base + UHCI_USBINTR); | 
|  | 121 | if (intr & (~UHCI_USBINTR_RESUME)) { | 
|  | 122 | dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n", | 
|  | 123 | __FUNCTION__, intr); | 
|  | 124 | goto reset_needed; | 
|  | 125 | } | 
|  | 126 | return 0; | 
|  | 127 |  | 
|  | 128 | reset_needed: | 
|  | 129 | dev_dbg(&pdev->dev, "Performing full reset\n"); | 
|  | 130 | uhci_reset_hc(pdev, base); | 
|  | 131 | return 1; | 
|  | 132 | } | 
|  | 133 | EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); | 
|  | 134 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 135 | static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) | 
|  | 136 | { | 
|  | 137 | u16 cmd; | 
|  | 138 | return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask); | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | #define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO) | 
|  | 142 | #define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY) | 
|  | 143 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 144 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) | 
|  | 145 | { | 
|  | 146 | unsigned long base = 0; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 147 | int i; | 
|  | 148 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 149 | if (!pio_enabled(pdev)) | 
|  | 150 | return; | 
|  | 151 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 152 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | 
|  | 153 | if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { | 
|  | 154 | base = pci_resource_start(pdev, i); | 
|  | 155 | break; | 
|  | 156 | } | 
|  | 157 |  | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 158 | if (base) | 
|  | 159 | uhci_check_and_reset_hc(pdev, base); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 160 | } | 
|  | 161 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 162 | static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx) | 
|  | 163 | { | 
|  | 164 | return pci_resource_start(pdev, idx) && mmio_enabled(pdev); | 
|  | 165 | } | 
|  | 166 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 167 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | 
|  | 168 | { | 
|  | 169 | void __iomem *base; | 
|  | 170 | int wait_time; | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 171 | u32 control; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 172 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 173 | if (!mmio_resource_enabled(pdev, 0)) | 
|  | 174 | return; | 
|  | 175 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 176 | base = ioremap_nocache(pci_resource_start(pdev, 0), | 
|  | 177 | pci_resource_len(pdev, 0)); | 
|  | 178 | if (base == NULL) return; | 
|  | 179 |  | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 180 | /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ | 
|  | 181 | #ifndef __hppa__ | 
|  | 182 | control = readl(base + OHCI_CONTROL); | 
|  | 183 | if (control & OHCI_CTRL_IR) { | 
|  | 184 | wait_time = 500; /* arbitrary; 5 seconds */ | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 185 | writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); | 
|  | 186 | writel(OHCI_OCR, base + OHCI_CMDSTATUS); | 
|  | 187 | while (wait_time > 0 && | 
|  | 188 | readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { | 
|  | 189 | wait_time -= 10; | 
|  | 190 | msleep(10); | 
|  | 191 | } | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 192 | if (wait_time <= 0) | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 193 | printk(KERN_WARNING "%s %s: BIOS handoff " | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 194 | "failed (BIOS bug ?)\n", | 
|  | 195 | pdev->dev.bus_id, "OHCI"); | 
|  | 196 |  | 
|  | 197 | /* reset controller, preserving RWC */ | 
|  | 198 | writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 199 | } | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 200 | #endif | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 201 |  | 
|  | 202 | /* | 
|  | 203 | * disable interrupts | 
|  | 204 | */ | 
|  | 205 | writel(~(u32)0, base + OHCI_INTRDISABLE); | 
|  | 206 | writel(~(u32)0, base + OHCI_INTRSTATUS); | 
|  | 207 |  | 
|  | 208 | iounmap(base); | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | 
|  | 212 | { | 
|  | 213 | int wait_time, delta; | 
|  | 214 | void __iomem *base, *op_reg_base; | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 215 | u32	hcc_params, val; | 
|  | 216 | u8	offset, cap_length; | 
|  | 217 | int	count = 256/4; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 218 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 219 | if (!mmio_resource_enabled(pdev, 0)) | 
|  | 220 | return; | 
|  | 221 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 222 | base = ioremap_nocache(pci_resource_start(pdev, 0), | 
|  | 223 | pci_resource_len(pdev, 0)); | 
|  | 224 | if (base == NULL) return; | 
|  | 225 |  | 
|  | 226 | cap_length = readb(base); | 
|  | 227 | op_reg_base = base + cap_length; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 228 |  | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 229 | /* EHCI 0.96 and later may have "extended capabilities" | 
|  | 230 | * spec section 5.1 explains the bios handoff, e.g. for | 
|  | 231 | * booting from USB disk or using a usb keyboard | 
|  | 232 | */ | 
|  | 233 | hcc_params = readl(base + EHCI_HCC_PARAMS); | 
|  | 234 | offset = (hcc_params >> 8) & 0xff; | 
|  | 235 | while (offset && count--) { | 
|  | 236 | u32		cap; | 
|  | 237 | int		msec; | 
|  | 238 |  | 
|  | 239 | pci_read_config_dword(pdev, offset, &cap); | 
|  | 240 | switch (cap & 0xff) { | 
|  | 241 | case 1:			/* BIOS/SMM/... handoff support */ | 
|  | 242 | if ((cap & EHCI_USBLEGSUP_BIOS)) { | 
|  | 243 | pr_debug("%s %s: BIOS handoff\n", | 
|  | 244 | pdev->dev.bus_id, "EHCI"); | 
|  | 245 |  | 
|  | 246 | /* BIOS workaround (?): be sure the | 
|  | 247 | * pre-Linux code receives the SMI | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 248 | */ | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 249 | pci_read_config_dword(pdev, | 
|  | 250 | offset + EHCI_USBLEGCTLSTS, | 
|  | 251 | &val); | 
|  | 252 | pci_write_config_dword(pdev, | 
|  | 253 | offset + EHCI_USBLEGCTLSTS, | 
|  | 254 | val | EHCI_USBLEGCTLSTS_SOOE); | 
|  | 255 | } | 
|  | 256 |  | 
|  | 257 | /* always say Linux will own the hardware | 
|  | 258 | * by setting EHCI_USBLEGSUP_OS. | 
|  | 259 | */ | 
|  | 260 | pci_write_config_byte(pdev, offset + 3, 1); | 
|  | 261 |  | 
|  | 262 | /* if boot firmware now owns EHCI, spin till | 
|  | 263 | * it hands it over. | 
|  | 264 | */ | 
|  | 265 | msec = 5000; | 
|  | 266 | while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { | 
|  | 267 | msleep(10); | 
|  | 268 | msec -= 10; | 
|  | 269 | pci_read_config_dword(pdev, offset, &cap); | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | if (cap & EHCI_USBLEGSUP_BIOS) { | 
|  | 273 | /* well, possibly buggy BIOS... try to shut | 
|  | 274 | * it down, and hope nothing goes too wrong | 
|  | 275 | */ | 
|  | 276 | printk(KERN_WARNING "%s %s: BIOS handoff " | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 277 | "failed (BIOS bug ?)\n", | 
|  | 278 | pdev->dev.bus_id, "EHCI"); | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 279 | pci_write_config_byte(pdev, offset + 2, 0); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 280 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 281 |  | 
|  | 282 | /* just in case, always disable EHCI SMIs */ | 
|  | 283 | pci_write_config_dword(pdev, | 
|  | 284 | offset + EHCI_USBLEGCTLSTS, | 
|  | 285 | 0); | 
|  | 286 | break; | 
|  | 287 | case 0:			/* illegal reserved capability */ | 
|  | 288 | cap = 0; | 
|  | 289 | /* FALLTHROUGH */ | 
|  | 290 | default: | 
|  | 291 | printk(KERN_WARNING "%s %s: unrecognized " | 
|  | 292 | "capability %02x\n", | 
|  | 293 | pdev->dev.bus_id, "EHCI", | 
|  | 294 | cap & 0xff); | 
|  | 295 | break; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 296 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 297 | offset = (cap >> 8) & 0xff; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 298 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 299 | if (!count) | 
|  | 300 | printk(KERN_DEBUG "%s %s: capability loop?\n", | 
|  | 301 | pdev->dev.bus_id, "EHCI"); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 302 |  | 
|  | 303 | /* | 
|  | 304 | * halt EHCI & disable its interrupts in any case | 
|  | 305 | */ | 
|  | 306 | val = readl(op_reg_base + EHCI_USBSTS); | 
|  | 307 | if ((val & EHCI_USBSTS_HALTED) == 0) { | 
|  | 308 | val = readl(op_reg_base + EHCI_USBCMD); | 
|  | 309 | val &= ~EHCI_USBCMD_RUN; | 
|  | 310 | writel(val, op_reg_base + EHCI_USBCMD); | 
|  | 311 |  | 
|  | 312 | wait_time = 2000; | 
|  | 313 | delta = 100; | 
|  | 314 | do { | 
|  | 315 | writel(0x3f, op_reg_base + EHCI_USBSTS); | 
|  | 316 | udelay(delta); | 
|  | 317 | wait_time -= delta; | 
|  | 318 | val = readl(op_reg_base + EHCI_USBSTS); | 
|  | 319 | if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { | 
|  | 320 | break; | 
|  | 321 | } | 
|  | 322 | } while (wait_time > 0); | 
|  | 323 | } | 
|  | 324 | writel(0, op_reg_base + EHCI_USBINTR); | 
|  | 325 | writel(0x3f, op_reg_base + EHCI_USBSTS); | 
|  | 326 |  | 
|  | 327 | iounmap(base); | 
|  | 328 |  | 
|  | 329 | return; | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 |  | 
|  | 333 |  | 
|  | 334 | static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) | 
|  | 335 | { | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 336 | if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 337 | quirk_usb_handoff_uhci(pdev); | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 338 | else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 339 | quirk_usb_handoff_ohci(pdev); | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 340 | else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 341 | quirk_usb_disable_ehci(pdev); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 342 | } | 
| David S. Miller | 462aae6 | 2005-11-04 11:17:24 -0800 | [diff] [blame] | 343 | DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); |