diff options
author | David Brownell <david-b@pacbell.net> | 2006-01-24 10:15:30 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-31 20:23:35 -0500 |
commit | 401feafa621ba98ecaeed5db1a53ab878943c225 (patch) | |
tree | 225ffa420c626ca5c4525a96f476706cd95f8933 /drivers/usb | |
parent | 3fa97c9db4f6f93f41f7a40d08872dbfd8dc907e (diff) |
[PATCH] USB: fix EHCI early handoff issues
This moves the previously widely-used ehci-pci.c BIOS handoff
code into the pci-quirks.c file, replacing the less widely used
"early handoff" version that seems to cause problems lately.
One notable change: the "early handoff" version always enabled
an SMI IRQ ... and did so even if the pre-Linux code said it was
not using EHCI (and not expecting EHCI SMIs). Looks like a goof
in a workaround for some unknown BIOS version.
This merged version only forcibly enables those IRQs when pre-Linux
code says it's using EHCI. And now it always forces them off "just
in case".
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 63 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 106 |
2 files changed, 71 insertions, 98 deletions
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 08ca0f849dab..286b2f4acdc2 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -24,40 +24,6 @@ | |||
24 | 24 | ||
25 | /*-------------------------------------------------------------------------*/ | 25 | /*-------------------------------------------------------------------------*/ |
26 | 26 | ||
27 | /* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... | ||
28 | * off the controller (maybe it can boot from highspeed USB disks). | ||
29 | */ | ||
30 | static int bios_handoff(struct ehci_hcd *ehci, int where, u32 cap) | ||
31 | { | ||
32 | struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); | ||
33 | |||
34 | /* always say Linux will own the hardware */ | ||
35 | pci_write_config_byte(pdev, where + 3, 1); | ||
36 | |||
37 | /* maybe wait a while for BIOS to respond */ | ||
38 | if (cap & (1 << 16)) { | ||
39 | int msec = 5000; | ||
40 | |||
41 | do { | ||
42 | msleep(10); | ||
43 | msec -= 10; | ||
44 | pci_read_config_dword(pdev, where, &cap); | ||
45 | } while ((cap & (1 << 16)) && msec); | ||
46 | if (cap & (1 << 16)) { | ||
47 | ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n", | ||
48 | where, cap); | ||
49 | // some BIOS versions seem buggy... | ||
50 | // return 1; | ||
51 | ehci_warn(ehci, "continuing after BIOS bug...\n"); | ||
52 | /* disable all SMIs, and clear "BIOS owns" flag */ | ||
53 | pci_write_config_dword(pdev, where + 4, 0); | ||
54 | pci_write_config_byte(pdev, where + 2, 0); | ||
55 | } else | ||
56 | ehci_dbg(ehci, "BIOS handoff succeeded\n"); | ||
57 | } | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* called after powerup, by probe or system-pm "wakeup" */ | 27 | /* called after powerup, by probe or system-pm "wakeup" */ |
62 | static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) | 28 | static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) |
63 | { | 29 | { |
@@ -84,32 +50,9 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) | |||
84 | } | 50 | } |
85 | } | 51 | } |
86 | 52 | ||
87 | temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params)); | 53 | /* we expect static quirk code to handle the "extended capabilities" |
88 | 54 | * (currently just BIOS handoff) allowed starting with EHCI 0.96 | |
89 | /* EHCI 0.96 and later may have "extended capabilities" */ | 55 | */ |
90 | while (temp && count--) { | ||
91 | u32 cap; | ||
92 | |||
93 | pci_read_config_dword(pdev, temp, &cap); | ||
94 | ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp); | ||
95 | switch (cap & 0xff) { | ||
96 | case 1: /* BIOS/SMM/... handoff */ | ||
97 | if (bios_handoff(ehci, temp, cap) != 0) | ||
98 | return -EOPNOTSUPP; | ||
99 | break; | ||
100 | case 0: /* illegal reserved capability */ | ||
101 | ehci_dbg(ehci, "illegal capability!\n"); | ||
102 | cap = 0; | ||
103 | /* FALLTHROUGH */ | ||
104 | default: /* unknown */ | ||
105 | break; | ||
106 | } | ||
107 | temp = (cap >> 8) & 0xff; | ||
108 | } | ||
109 | if (!count) { | ||
110 | ehci_err(ehci, "bogus capabilities ... PCI problems!\n"); | ||
111 | return -EIO; | ||
112 | } | ||
113 | 56 | ||
114 | /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */ | 57 | /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */ |
115 | retval = pci_set_mwi(pdev); | 58 | retval = pci_set_mwi(pdev); |
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 |