| 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 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 11 | #include <linux/types.h> | 
|  | 12 | #include <linux/kernel.h> | 
|  | 13 | #include <linux/pci.h> | 
|  | 14 | #include <linux/init.h> | 
|  | 15 | #include <linux/delay.h> | 
|  | 16 | #include <linux/acpi.h> | 
| Adrian Bunk | 75e2df6 | 2006-03-25 18:01:53 +0100 | [diff] [blame] | 17 | #include "pci-quirks.h" | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 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 */ | 
| Alan Stern | 4fe5354 | 2007-04-05 16:06:53 -0400 | [diff] [blame] | 47 | #define EHCI_CONFIGFLAG		0x40		/* configured flag register */ | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 48 | #define EHCI_USBLEGSUP		0		/* legacy support register */ | 
|  | 49 | #define EHCI_USBLEGSUP_BIOS	(1 << 16)	/* BIOS semaphore */ | 
|  | 50 | #define EHCI_USBLEGSUP_OS	(1 << 24)	/* OS semaphore */ | 
|  | 51 | #define EHCI_USBLEGCTLSTS	4		/* legacy control/status */ | 
|  | 52 | #define EHCI_USBLEGCTLSTS_SOOE	(1 << 13)	/* SMI on ownership change */ | 
|  | 53 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 54 |  | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 55 | /* | 
|  | 56 | * Make sure the controller is completely inactive, unable to | 
|  | 57 | * generate interrupts or do DMA. | 
|  | 58 | */ | 
|  | 59 | void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) | 
|  | 60 | { | 
|  | 61 | /* Turn off PIRQ enable and SMI enable.  (This also turns off the | 
|  | 62 | * BIOS's USB Legacy Support.)  Turn off all the R/WC bits too. | 
|  | 63 | */ | 
|  | 64 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC); | 
|  | 65 |  | 
|  | 66 | /* Reset the HC - this will force us to get a | 
|  | 67 | * new notification of any already connected | 
|  | 68 | * ports due to the virtual disconnect that it | 
|  | 69 | * implies. | 
|  | 70 | */ | 
|  | 71 | outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD); | 
|  | 72 | mb(); | 
|  | 73 | udelay(5); | 
|  | 74 | if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) | 
|  | 75 | dev_warn(&pdev->dev, "HCRESET not completed yet!\n"); | 
|  | 76 |  | 
|  | 77 | /* Just to be safe, disable interrupt requests and | 
|  | 78 | * make sure the controller is stopped. | 
|  | 79 | */ | 
|  | 80 | outw(0, base + UHCI_USBINTR); | 
|  | 81 | outw(0, base + UHCI_USBCMD); | 
|  | 82 | } | 
|  | 83 | EXPORT_SYMBOL_GPL(uhci_reset_hc); | 
|  | 84 |  | 
|  | 85 | /* | 
|  | 86 | * Initialize a controller that was newly discovered or has just been | 
|  | 87 | * resumed.  In either case we can't be sure of its previous state. | 
|  | 88 | * | 
|  | 89 | * Returns: 1 if the controller was reset, 0 otherwise. | 
|  | 90 | */ | 
|  | 91 | int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) | 
|  | 92 | { | 
|  | 93 | u16 legsup; | 
|  | 94 | unsigned int cmd, intr; | 
|  | 95 |  | 
|  | 96 | /* | 
|  | 97 | * When restarting a suspended controller, we expect all the | 
|  | 98 | * settings to be the same as we left them: | 
|  | 99 | * | 
|  | 100 | *	PIRQ and SMI disabled, no R/W bits set in USBLEGSUP; | 
|  | 101 | *	Controller is stopped and configured with EGSM set; | 
|  | 102 | *	No interrupts enabled except possibly Resume Detect. | 
|  | 103 | * | 
|  | 104 | * If any of these conditions are violated we do a complete reset. | 
|  | 105 | */ | 
|  | 106 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup); | 
|  | 107 | if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) { | 
|  | 108 | dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n", | 
| Harvey Harrison | 441b62c | 2008-03-03 16:08:34 -0800 | [diff] [blame] | 109 | __func__, legsup); | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 110 | goto reset_needed; | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | cmd = inw(base + UHCI_USBCMD); | 
|  | 114 | if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) || | 
|  | 115 | !(cmd & UHCI_USBCMD_EGSM)) { | 
|  | 116 | dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n", | 
| Harvey Harrison | 441b62c | 2008-03-03 16:08:34 -0800 | [diff] [blame] | 117 | __func__, cmd); | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 118 | goto reset_needed; | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | intr = inw(base + UHCI_USBINTR); | 
|  | 122 | if (intr & (~UHCI_USBINTR_RESUME)) { | 
|  | 123 | dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n", | 
| Harvey Harrison | 441b62c | 2008-03-03 16:08:34 -0800 | [diff] [blame] | 124 | __func__, intr); | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 125 | goto reset_needed; | 
|  | 126 | } | 
|  | 127 | return 0; | 
|  | 128 |  | 
|  | 129 | reset_needed: | 
|  | 130 | dev_dbg(&pdev->dev, "Performing full reset\n"); | 
|  | 131 | uhci_reset_hc(pdev, base); | 
|  | 132 | return 1; | 
|  | 133 | } | 
|  | 134 | EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); | 
|  | 135 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 136 | static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) | 
|  | 137 | { | 
|  | 138 | u16 cmd; | 
|  | 139 | return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask); | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | #define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO) | 
|  | 143 | #define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY) | 
|  | 144 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 145 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) | 
|  | 146 | { | 
|  | 147 | unsigned long base = 0; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 148 | int i; | 
|  | 149 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 150 | if (!pio_enabled(pdev)) | 
|  | 151 | return; | 
|  | 152 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 153 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | 
|  | 154 | if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { | 
|  | 155 | base = pci_resource_start(pdev, i); | 
|  | 156 | break; | 
|  | 157 | } | 
|  | 158 |  | 
| Alan Stern | bb200f6 | 2005-10-03 16:36:29 -0400 | [diff] [blame] | 159 | if (base) | 
|  | 160 | uhci_check_and_reset_hc(pdev, base); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 161 | } | 
|  | 162 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 163 | static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx) | 
|  | 164 | { | 
|  | 165 | return pci_resource_start(pdev, idx) && mmio_enabled(pdev); | 
|  | 166 | } | 
|  | 167 |  | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 168 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | 
|  | 169 | { | 
|  | 170 | void __iomem *base; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 171 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 172 | if (!mmio_resource_enabled(pdev, 0)) | 
|  | 173 | return; | 
|  | 174 |  | 
| Arjan van de Ven | 8e8ce4b | 2008-10-20 21:46:01 -0700 | [diff] [blame] | 175 | base = pci_ioremap_bar(pdev, 0); | 
|  | 176 | if (base == NULL) | 
|  | 177 | return; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 178 |  | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 179 | /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ | 
|  | 180 | #ifndef __hppa__ | 
| Kyle McMartin | c1b45f2 | 2006-06-25 18:45:29 -0400 | [diff] [blame] | 181 | { | 
|  | 182 | u32 control = readl(base + OHCI_CONTROL); | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 183 | if (control & OHCI_CTRL_IR) { | 
| Kyle McMartin | c1b45f2 | 2006-06-25 18:45:29 -0400 | [diff] [blame] | 184 | int 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) | 
| bjorn.helgaas@hp.com | f0fda80 | 2007-12-17 14:09:39 -0700 | [diff] [blame] | 193 | dev_warn(&pdev->dev, "OHCI: BIOS handoff failed" | 
|  | 194 | " (BIOS bug?) %08x\n", | 
| David Brownell | a38408c | 2006-02-09 16:35:31 -0500 | [diff] [blame] | 195 | readl(base + OHCI_CONTROL)); | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 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 | } | 
| Kyle McMartin | c1b45f2 | 2006-06-25 18:45:29 -0400 | [diff] [blame] | 200 | } | 
| David Brownell | f2cb36c | 2005-09-22 22:43:30 -0700 | [diff] [blame] | 201 | #endif | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 202 |  | 
|  | 203 | /* | 
|  | 204 | * disable interrupts | 
|  | 205 | */ | 
|  | 206 | writel(~(u32)0, base + OHCI_INTRDISABLE); | 
|  | 207 | writel(~(u32)0, base + OHCI_INTRSTATUS); | 
|  | 208 |  | 
|  | 209 | iounmap(base); | 
|  | 210 | } | 
|  | 211 |  | 
|  | 212 | static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | 
|  | 213 | { | 
|  | 214 | int wait_time, delta; | 
|  | 215 | void __iomem *base, *op_reg_base; | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 216 | u32	hcc_params, val; | 
|  | 217 | u8	offset, cap_length; | 
|  | 218 | int	count = 256/4; | 
| Alan Stern | 4fe5354 | 2007-04-05 16:06:53 -0400 | [diff] [blame] | 219 | int	tried_handoff = 0; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 220 |  | 
| Linus Torvalds | 541ab4a | 2005-10-31 21:12:40 -0800 | [diff] [blame] | 221 | if (!mmio_resource_enabled(pdev, 0)) | 
|  | 222 | return; | 
|  | 223 |  | 
| Arjan van de Ven | 8e8ce4b | 2008-10-20 21:46:01 -0700 | [diff] [blame] | 224 | base = pci_ioremap_bar(pdev, 0); | 
|  | 225 | if (base == NULL) | 
|  | 226 | return; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 227 |  | 
|  | 228 | cap_length = readb(base); | 
|  | 229 | op_reg_base = base + cap_length; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 230 |  | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 231 | /* EHCI 0.96 and later may have "extended capabilities" | 
|  | 232 | * spec section 5.1 explains the bios handoff, e.g. for | 
|  | 233 | * booting from USB disk or using a usb keyboard | 
|  | 234 | */ | 
|  | 235 | hcc_params = readl(base + EHCI_HCC_PARAMS); | 
|  | 236 | offset = (hcc_params >> 8) & 0xff; | 
| Roel Kluin | 6e14bda | 2009-01-31 12:37:04 +0100 | [diff] [blame] | 237 | while (offset && --count) { | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 238 | u32		cap; | 
|  | 239 | int		msec; | 
|  | 240 |  | 
|  | 241 | pci_read_config_dword(pdev, offset, &cap); | 
|  | 242 | switch (cap & 0xff) { | 
|  | 243 | case 1:			/* BIOS/SMM/... handoff support */ | 
|  | 244 | if ((cap & EHCI_USBLEGSUP_BIOS)) { | 
| bjorn.helgaas@hp.com | f0fda80 | 2007-12-17 14:09:39 -0700 | [diff] [blame] | 245 | dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 246 |  | 
| David Brownell | a38408c | 2006-02-09 16:35:31 -0500 | [diff] [blame] | 247 | #if 0 | 
|  | 248 | /* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on, | 
|  | 249 | * but that seems dubious in general (the BIOS left it off intentionally) | 
|  | 250 | * and is known to prevent some systems from booting.  so we won't do this | 
|  | 251 | * unless maybe we can determine when we're on a system that needs SMI forced. | 
|  | 252 | */ | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 253 | /* BIOS workaround (?): be sure the | 
|  | 254 | * pre-Linux code receives the SMI | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 255 | */ | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 256 | pci_read_config_dword(pdev, | 
|  | 257 | offset + EHCI_USBLEGCTLSTS, | 
|  | 258 | &val); | 
|  | 259 | pci_write_config_dword(pdev, | 
|  | 260 | offset + EHCI_USBLEGCTLSTS, | 
|  | 261 | val | EHCI_USBLEGCTLSTS_SOOE); | 
| David Brownell | a38408c | 2006-02-09 16:35:31 -0500 | [diff] [blame] | 262 | #endif | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 263 |  | 
| David Brownell | 8c45080 | 2006-02-24 16:55:52 -0800 | [diff] [blame] | 264 | /* some systems get upset if this semaphore is | 
|  | 265 | * set for any other reason than forcing a BIOS | 
|  | 266 | * handoff.. | 
|  | 267 | */ | 
|  | 268 | pci_write_config_byte(pdev, offset + 3, 1); | 
|  | 269 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 270 |  | 
|  | 271 | /* if boot firmware now owns EHCI, spin till | 
|  | 272 | * it hands it over. | 
|  | 273 | */ | 
| Steven Noonan | d859bff | 2008-11-05 12:41:24 -0800 | [diff] [blame] | 274 | msec = 1000; | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 275 | while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { | 
| Alan Stern | 4fe5354 | 2007-04-05 16:06:53 -0400 | [diff] [blame] | 276 | tried_handoff = 1; | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 277 | msleep(10); | 
|  | 278 | msec -= 10; | 
|  | 279 | pci_read_config_dword(pdev, offset, &cap); | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | if (cap & EHCI_USBLEGSUP_BIOS) { | 
|  | 283 | /* well, possibly buggy BIOS... try to shut | 
|  | 284 | * it down, and hope nothing goes too wrong | 
|  | 285 | */ | 
| bjorn.helgaas@hp.com | f0fda80 | 2007-12-17 14:09:39 -0700 | [diff] [blame] | 286 | dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" | 
|  | 287 | " (BIOS bug?) %08x\n", cap); | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 288 | pci_write_config_byte(pdev, offset + 2, 0); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 289 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 290 |  | 
|  | 291 | /* just in case, always disable EHCI SMIs */ | 
|  | 292 | pci_write_config_dword(pdev, | 
|  | 293 | offset + EHCI_USBLEGCTLSTS, | 
|  | 294 | 0); | 
| Alan Stern | 4fe5354 | 2007-04-05 16:06:53 -0400 | [diff] [blame] | 295 |  | 
|  | 296 | /* If the BIOS ever owned the controller then we | 
|  | 297 | * can't expect any power sessions to remain intact. | 
|  | 298 | */ | 
|  | 299 | if (tried_handoff) | 
|  | 300 | writel(0, op_reg_base + EHCI_CONFIGFLAG); | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 301 | break; | 
|  | 302 | case 0:			/* illegal reserved capability */ | 
|  | 303 | cap = 0; | 
|  | 304 | /* FALLTHROUGH */ | 
|  | 305 | default: | 
| bjorn.helgaas@hp.com | f0fda80 | 2007-12-17 14:09:39 -0700 | [diff] [blame] | 306 | dev_warn(&pdev->dev, "EHCI: unrecognized capability " | 
|  | 307 | "%02x\n", cap & 0xff); | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 308 | break; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 309 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 310 | offset = (cap >> 8) & 0xff; | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 311 | } | 
| David Brownell | 401feaf | 2006-01-24 07:15:30 -0800 | [diff] [blame] | 312 | if (!count) | 
| bjorn.helgaas@hp.com | f0fda80 | 2007-12-17 14:09:39 -0700 | [diff] [blame] | 313 | dev_printk(KERN_DEBUG, &pdev->dev, "EHCI: capability loop?\n"); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 314 |  | 
|  | 315 | /* | 
|  | 316 | * halt EHCI & disable its interrupts in any case | 
|  | 317 | */ | 
|  | 318 | val = readl(op_reg_base + EHCI_USBSTS); | 
|  | 319 | if ((val & EHCI_USBSTS_HALTED) == 0) { | 
|  | 320 | val = readl(op_reg_base + EHCI_USBCMD); | 
|  | 321 | val &= ~EHCI_USBCMD_RUN; | 
|  | 322 | writel(val, op_reg_base + EHCI_USBCMD); | 
|  | 323 |  | 
|  | 324 | wait_time = 2000; | 
|  | 325 | delta = 100; | 
|  | 326 | do { | 
|  | 327 | writel(0x3f, op_reg_base + EHCI_USBSTS); | 
|  | 328 | udelay(delta); | 
|  | 329 | wait_time -= delta; | 
|  | 330 | val = readl(op_reg_base + EHCI_USBSTS); | 
|  | 331 | if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { | 
|  | 332 | break; | 
|  | 333 | } | 
|  | 334 | } while (wait_time > 0); | 
|  | 335 | } | 
|  | 336 | writel(0, op_reg_base + EHCI_USBINTR); | 
|  | 337 | writel(0x3f, op_reg_base + EHCI_USBSTS); | 
|  | 338 |  | 
|  | 339 | iounmap(base); | 
|  | 340 |  | 
|  | 341 | return; | 
|  | 342 | } | 
|  | 343 |  | 
|  | 344 |  | 
|  | 345 |  | 
|  | 346 | static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) | 
|  | 347 | { | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 348 | if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 349 | quirk_usb_handoff_uhci(pdev); | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 350 | else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 351 | quirk_usb_handoff_ohci(pdev); | 
| Alan Stern | 478a3ba | 2005-10-19 12:52:02 -0400 | [diff] [blame] | 352 | else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI) | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 353 | quirk_usb_disable_ehci(pdev); | 
| David Brownell | 7586269 | 2005-09-23 17:14:37 -0700 | [diff] [blame] | 354 | } | 
| David S. Miller | 462aae6 | 2005-11-04 11:17:24 -0800 | [diff] [blame] | 355 | DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); |