Commit 401feafa authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3fa97c9d
...@@ -24,40 +24,6 @@ ...@@ -24,40 +24,6 @@
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
* off the controller (maybe it can boot from highspeed USB disks).
*/
static int bios_handoff(struct ehci_hcd *ehci, int where, u32 cap)
{
struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
/* always say Linux will own the hardware */
pci_write_config_byte(pdev, where + 3, 1);
/* maybe wait a while for BIOS to respond */
if (cap & (1 << 16)) {
int msec = 5000;
do {
msleep(10);
msec -= 10;
pci_read_config_dword(pdev, where, &cap);
} while ((cap & (1 << 16)) && msec);
if (cap & (1 << 16)) {
ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n",
where, cap);
// some BIOS versions seem buggy...
// return 1;
ehci_warn(ehci, "continuing after BIOS bug...\n");
/* disable all SMIs, and clear "BIOS owns" flag */
pci_write_config_dword(pdev, where + 4, 0);
pci_write_config_byte(pdev, where + 2, 0);
} else
ehci_dbg(ehci, "BIOS handoff succeeded\n");
}
return 0;
}
/* called after powerup, by probe or system-pm "wakeup" */ /* called after powerup, by probe or system-pm "wakeup" */
static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
{ {
...@@ -84,32 +50,9 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) ...@@ -84,32 +50,9 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
} }
} }
temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params)); /* we expect static quirk code to handle the "extended capabilities"
* (currently just BIOS handoff) allowed starting with EHCI 0.96
/* EHCI 0.96 and later may have "extended capabilities" */ */
while (temp && count--) {
u32 cap;
pci_read_config_dword(pdev, temp, &cap);
ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
switch (cap & 0xff) {
case 1: /* BIOS/SMM/... handoff */
if (bios_handoff(ehci, temp, cap) != 0)
return -EOPNOTSUPP;
break;
case 0: /* illegal reserved capability */
ehci_dbg(ehci, "illegal capability!\n");
cap = 0;
/* FALLTHROUGH */
default: /* unknown */
break;
}
temp = (cap >> 8) & 0xff;
}
if (!count) {
ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
return -EIO;
}
/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */ /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
retval = pci_set_mwi(pdev); retval = pci_set_mwi(pdev);
......
...@@ -190,7 +190,7 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) ...@@ -190,7 +190,7 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
msleep(10); msleep(10);
} }
if (wait_time <= 0) if (wait_time <= 0)
printk(KERN_WARNING "%s %s: early BIOS handoff " printk(KERN_WARNING "%s %s: BIOS handoff "
"failed (BIOS bug ?)\n", "failed (BIOS bug ?)\n",
pdev->dev.bus_id, "OHCI"); pdev->dev.bus_id, "OHCI");
...@@ -212,8 +212,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) ...@@ -212,8 +212,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
{ {
int wait_time, delta; int wait_time, delta;
void __iomem *base, *op_reg_base; void __iomem *base, *op_reg_base;
u32 hcc_params, val, temp; u32 hcc_params, val;
u8 cap_length; u8 offset, cap_length;
int count = 256/4;
if (!mmio_resource_enabled(pdev, 0)) if (!mmio_resource_enabled(pdev, 0))
return; return;
...@@ -224,51 +225,80 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) ...@@ -224,51 +225,80 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
cap_length = readb(base); cap_length = readb(base);
op_reg_base = base + cap_length; op_reg_base = base + cap_length;
/* EHCI 0.96 and later may have "extended capabilities"
* spec section 5.1 explains the bios handoff, e.g. for
* booting from USB disk or using a usb keyboard
*/
hcc_params = readl(base + EHCI_HCC_PARAMS); hcc_params = readl(base + EHCI_HCC_PARAMS);
hcc_params = (hcc_params >> 8) & 0xff; offset = (hcc_params >> 8) & 0xff;
if (hcc_params) { while (offset && count--) {
pci_read_config_dword(pdev, u32 cap;
hcc_params + EHCI_USBLEGSUP, int msec;
&val);
if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { pci_read_config_dword(pdev, offset, &cap);
/* switch (cap & 0xff) {
* Ok, BIOS is in smm mode, try to hand off... case 1: /* BIOS/SMM/... handoff support */
if ((cap & EHCI_USBLEGSUP_BIOS)) {
pr_debug("%s %s: BIOS handoff\n",
pdev->dev.bus_id, "EHCI");
/* BIOS workaround (?): be sure the
* pre-Linux code receives the SMI
*/ */
pci_read_config_dword(pdev, pci_read_config_dword(pdev,
hcc_params + EHCI_USBLEGCTLSTS, offset + EHCI_USBLEGCTLSTS,
&temp); &val);
pci_write_config_dword(pdev,
hcc_params + EHCI_USBLEGCTLSTS,
temp | EHCI_USBLEGCTLSTS_SOOE);
val |= EHCI_USBLEGSUP_OS;
pci_write_config_dword(pdev, pci_write_config_dword(pdev,
hcc_params + EHCI_USBLEGSUP, offset + EHCI_USBLEGCTLSTS,
val); val | EHCI_USBLEGCTLSTS_SOOE);
}
wait_time = 500; /* always say Linux will own the hardware
do { * by setting EHCI_USBLEGSUP_OS.
*/
pci_write_config_byte(pdev, offset + 3, 1);
/* if boot firmware now owns EHCI, spin till
* it hands it over.
*/
msec = 5000;
while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
msleep(10); msleep(10);
wait_time -= 10; msec -= 10;
pci_read_config_dword(pdev, pci_read_config_dword(pdev, offset, &cap);
hcc_params + EHCI_USBLEGSUP, }
&val);
} while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); if (cap & EHCI_USBLEGSUP_BIOS) {
if (!wait_time) { /* well, possibly buggy BIOS... try to shut
/* * it down, and hope nothing goes too wrong
* well, possibly buggy BIOS...
*/ */
printk(KERN_WARNING "%s %s: early BIOS handoff " printk(KERN_WARNING "%s %s: BIOS handoff "
"failed (BIOS bug ?)\n", "failed (BIOS bug ?)\n",
pdev->dev.bus_id, "EHCI"); pdev->dev.bus_id, "EHCI");
pci_write_config_byte(pdev, offset + 2, 0);
}
/* just in case, always disable EHCI SMIs */
pci_write_config_dword(pdev, pci_write_config_dword(pdev,
hcc_params + EHCI_USBLEGSUP, offset + EHCI_USBLEGCTLSTS,
EHCI_USBLEGSUP_OS);
pci_write_config_dword(pdev,
hcc_params + EHCI_USBLEGCTLSTS,
0); 0);
break;
case 0: /* illegal reserved capability */
cap = 0;
/* FALLTHROUGH */
default:
printk(KERN_WARNING "%s %s: unrecognized "
"capability %02x\n",
pdev->dev.bus_id, "EHCI",
cap & 0xff);
break;
} }
offset = (cap >> 8) & 0xff;
} }
} if (!count)
printk(KERN_DEBUG "%s %s: capability loop?\n",
pdev->dev.bus_id, "EHCI");
/* /*
* halt EHCI & disable its interrupts in any case * halt EHCI & disable its interrupts in any case
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment