diff options
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 125 |
1 files changed, 82 insertions, 43 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 3ef2c0cdf1db..9e81c26313f9 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c | |||
@@ -190,9 +190,10 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | |||
190 | msleep(10); | 190 | msleep(10); |
191 | } | 191 | } |
192 | if (wait_time <= 0) | 192 | if (wait_time <= 0) |
193 | printk(KERN_WARNING "%s %s: early BIOS handoff " | 193 | printk(KERN_WARNING "%s %s: BIOS handoff " |
194 | "failed (BIOS bug ?)\n", | 194 | "failed (BIOS bug ?) %08x\n", |
195 | pdev->dev.bus_id, "OHCI"); | 195 | pdev->dev.bus_id, "OHCI", |
196 | readl(base + OHCI_CONTROL)); | ||
196 | 197 | ||
197 | /* reset controller, preserving RWC */ | 198 | /* reset controller, preserving RWC */ |
198 | writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL); | 199 | writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL); |
@@ -212,8 +213,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | |||
212 | { | 213 | { |
213 | int wait_time, delta; | 214 | int wait_time, delta; |
214 | void __iomem *base, *op_reg_base; | 215 | void __iomem *base, *op_reg_base; |
215 | u32 hcc_params, val, temp; | 216 | u32 hcc_params, val; |
216 | u8 cap_length; | 217 | u8 offset, cap_length; |
218 | int count = 256/4; | ||
217 | 219 | ||
218 | if (!mmio_resource_enabled(pdev, 0)) | 220 | if (!mmio_resource_enabled(pdev, 0)) |
219 | return; | 221 | return; |
@@ -224,51 +226,88 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | |||
224 | 226 | ||
225 | cap_length = readb(base); | 227 | cap_length = readb(base); |
226 | op_reg_base = base + cap_length; | 228 | op_reg_base = base + cap_length; |
227 | hcc_params = readl(base + EHCI_HCC_PARAMS); | ||
228 | hcc_params = (hcc_params >> 8) & 0xff; | ||
229 | if (hcc_params) { | ||
230 | pci_read_config_dword(pdev, | ||
231 | hcc_params + EHCI_USBLEGSUP, | ||
232 | &val); | ||
233 | if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { | ||
234 | /* | ||
235 | * Ok, BIOS is in smm mode, try to hand off... | ||
236 | */ | ||
237 | pci_read_config_dword(pdev, | ||
238 | hcc_params + EHCI_USBLEGCTLSTS, | ||
239 | &temp); | ||
240 | pci_write_config_dword(pdev, | ||
241 | hcc_params + EHCI_USBLEGCTLSTS, | ||
242 | temp | EHCI_USBLEGCTLSTS_SOOE); | ||
243 | val |= EHCI_USBLEGSUP_OS; | ||
244 | pci_write_config_dword(pdev, | ||
245 | hcc_params + EHCI_USBLEGSUP, | ||
246 | val); | ||
247 | 229 | ||
248 | wait_time = 500; | 230 | /* EHCI 0.96 and later may have "extended capabilities" |
249 | do { | 231 | * spec section 5.1 explains the bios handoff, e.g. for |
250 | msleep(10); | 232 | * booting from USB disk or using a usb keyboard |
251 | wait_time -= 10; | 233 | */ |
234 | hcc_params = readl(base + EHCI_HCC_PARAMS); | ||
235 | offset = (hcc_params >> 8) & 0xff; | ||
236 | while (offset && count--) { | ||
237 | u32 cap; | ||
238 | int msec; | ||
239 | |||
240 | pci_read_config_dword(pdev, offset, &cap); | ||
241 | switch (cap & 0xff) { | ||
242 | case 1: /* BIOS/SMM/... handoff support */ | ||
243 | if ((cap & EHCI_USBLEGSUP_BIOS)) { | ||
244 | pr_debug("%s %s: BIOS handoff\n", | ||
245 | pdev->dev.bus_id, "EHCI"); | ||
246 | |||
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 | */ | ||
253 | /* BIOS workaround (?): be sure the | ||
254 | * pre-Linux code receives the SMI | ||
255 | */ | ||
252 | pci_read_config_dword(pdev, | 256 | pci_read_config_dword(pdev, |
253 | hcc_params + EHCI_USBLEGSUP, | 257 | offset + EHCI_USBLEGCTLSTS, |
254 | &val); | 258 | &val); |
255 | } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); | ||
256 | if (!wait_time) { | ||
257 | /* | ||
258 | * well, possibly buggy BIOS... | ||
259 | */ | ||
260 | printk(KERN_WARNING "%s %s: early BIOS handoff " | ||
261 | "failed (BIOS bug ?)\n", | ||
262 | pdev->dev.bus_id, "EHCI"); | ||
263 | pci_write_config_dword(pdev, | 259 | pci_write_config_dword(pdev, |
264 | hcc_params + EHCI_USBLEGSUP, | 260 | offset + EHCI_USBLEGCTLSTS, |
265 | EHCI_USBLEGSUP_OS); | 261 | val | EHCI_USBLEGCTLSTS_SOOE); |
266 | pci_write_config_dword(pdev, | 262 | #endif |
267 | hcc_params + EHCI_USBLEGCTLSTS, | 263 | |
268 | 0); | 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 | } | ||
270 | |||
271 | /* if boot firmware now owns EHCI, spin till | ||
272 | * it hands it over. | ||
273 | */ | ||
274 | msec = 5000; | ||
275 | while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { | ||
276 | msleep(10); | ||
277 | msec -= 10; | ||
278 | pci_read_config_dword(pdev, offset, &cap); | ||
279 | } | ||
280 | |||
281 | if (cap & EHCI_USBLEGSUP_BIOS) { | ||
282 | /* well, possibly buggy BIOS... try to shut | ||
283 | * it down, and hope nothing goes too wrong | ||
284 | */ | ||
285 | printk(KERN_WARNING "%s %s: BIOS handoff " | ||
286 | "failed (BIOS bug ?) %08x\n", | ||
287 | pdev->dev.bus_id, "EHCI", cap); | ||
288 | pci_write_config_byte(pdev, offset + 2, 0); | ||
269 | } | 289 | } |
290 | |||
291 | /* just in case, always disable EHCI SMIs */ | ||
292 | pci_write_config_dword(pdev, | ||
293 | offset + EHCI_USBLEGCTLSTS, | ||
294 | 0); | ||
295 | break; | ||
296 | case 0: /* illegal reserved capability */ | ||
297 | cap = 0; | ||
298 | /* FALLTHROUGH */ | ||
299 | default: | ||
300 | printk(KERN_WARNING "%s %s: unrecognized " | ||
301 | "capability %02x\n", | ||
302 | pdev->dev.bus_id, "EHCI", | ||
303 | cap & 0xff); | ||
304 | break; | ||
270 | } | 305 | } |
306 | offset = (cap >> 8) & 0xff; | ||
271 | } | 307 | } |
308 | if (!count) | ||
309 | printk(KERN_DEBUG "%s %s: capability loop?\n", | ||
310 | pdev->dev.bus_id, "EHCI"); | ||
272 | 311 | ||
273 | /* | 312 | /* |
274 | * halt EHCI & disable its interrupts in any case | 313 | * halt EHCI & disable its interrupts in any case |