diff options
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
| -rw-r--r-- | drivers/usb/host/pci-quirks.c | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 3ef2c0cdf1db..e9e5bc178cef 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c | |||
| @@ -190,7 +190,7 @@ 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 ?)\n", |
| 195 | pdev->dev.bus_id, "OHCI"); | 195 | pdev->dev.bus_id, "OHCI"); |
| 196 | 196 | ||
| @@ -212,8 +212,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | |||
| 212 | { | 212 | { |
| 213 | int wait_time, delta; | 213 | int wait_time, delta; |
| 214 | void __iomem *base, *op_reg_base; | 214 | void __iomem *base, *op_reg_base; |
| 215 | u32 hcc_params, val, temp; | 215 | u32 hcc_params, val; |
| 216 | u8 cap_length; | 216 | u8 offset, cap_length; |
| 217 | int count = 256/4; | ||
| 217 | 218 | ||
| 218 | if (!mmio_resource_enabled(pdev, 0)) | 219 | if (!mmio_resource_enabled(pdev, 0)) |
| 219 | return; | 220 | return; |
| @@ -224,51 +225,80 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | |||
| 224 | 225 | ||
| 225 | cap_length = readb(base); | 226 | cap_length = readb(base); |
| 226 | op_reg_base = base + cap_length; | 227 | op_reg_base = base + cap_length; |
| 228 | |||
| 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 | */ | ||
| 227 | hcc_params = readl(base + EHCI_HCC_PARAMS); | 233 | hcc_params = readl(base + EHCI_HCC_PARAMS); |
| 228 | hcc_params = (hcc_params >> 8) & 0xff; | 234 | offset = (hcc_params >> 8) & 0xff; |
| 229 | if (hcc_params) { | 235 | while (offset && count--) { |
| 230 | pci_read_config_dword(pdev, | 236 | u32 cap; |
| 231 | hcc_params + EHCI_USBLEGSUP, | 237 | int msec; |
| 232 | &val); | 238 | |
| 233 | if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { | 239 | pci_read_config_dword(pdev, offset, &cap); |
| 234 | /* | 240 | switch (cap & 0xff) { |
| 235 | * Ok, BIOS is in smm mode, try to hand off... | 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 | ||
| 248 | */ | ||
| 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. | ||
| 236 | */ | 259 | */ |
| 237 | pci_read_config_dword(pdev, | 260 | pci_write_config_byte(pdev, offset + 3, 1); |
| 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 | 261 | ||
| 248 | wait_time = 500; | 262 | /* if boot firmware now owns EHCI, spin till |
| 249 | do { | 263 | * it hands it over. |
| 264 | */ | ||
| 265 | msec = 5000; | ||
| 266 | while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { | ||
| 250 | msleep(10); | 267 | msleep(10); |
| 251 | wait_time -= 10; | 268 | msec -= 10; |
| 252 | pci_read_config_dword(pdev, | 269 | pci_read_config_dword(pdev, offset, &cap); |
| 253 | hcc_params + EHCI_USBLEGSUP, | 270 | } |
| 254 | &val); | 271 | |
| 255 | } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); | 272 | if (cap & EHCI_USBLEGSUP_BIOS) { |
| 256 | if (!wait_time) { | 273 | /* well, possibly buggy BIOS... try to shut |
| 257 | /* | 274 | * it down, and hope nothing goes too wrong |
| 258 | * well, possibly buggy BIOS... | ||
| 259 | */ | 275 | */ |
| 260 | printk(KERN_WARNING "%s %s: early BIOS handoff " | 276 | printk(KERN_WARNING "%s %s: BIOS handoff " |
| 261 | "failed (BIOS bug ?)\n", | 277 | "failed (BIOS bug ?)\n", |
| 262 | pdev->dev.bus_id, "EHCI"); | 278 | pdev->dev.bus_id, "EHCI"); |
| 263 | pci_write_config_dword(pdev, | 279 | pci_write_config_byte(pdev, offset + 2, 0); |
| 264 | hcc_params + EHCI_USBLEGSUP, | ||
| 265 | EHCI_USBLEGSUP_OS); | ||
| 266 | pci_write_config_dword(pdev, | ||
| 267 | hcc_params + EHCI_USBLEGCTLSTS, | ||
| 268 | 0); | ||
| 269 | } | 280 | } |
| 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; | ||
| 270 | } | 296 | } |
| 297 | offset = (cap >> 8) & 0xff; | ||
| 271 | } | 298 | } |
| 299 | if (!count) | ||
| 300 | printk(KERN_DEBUG "%s %s: capability loop?\n", | ||
| 301 | pdev->dev.bus_id, "EHCI"); | ||
| 272 | 302 | ||
| 273 | /* | 303 | /* |
| 274 | * halt EHCI & disable its interrupts in any case | 304 | * halt EHCI & disable its interrupts in any case |
