summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-pci.c63
-rw-r--r--drivers/usb/host/pci-quirks.c106
2 files changed, 71 insertions, 98 deletions
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 08ca0f849da..286b2f4acdc 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -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" */
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));
-
- /* 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;
- }
+ /* we expect static quirk code to handle the "extended capabilities"
+ * (currently just BIOS handoff) allowed starting with EHCI 0.96
+ */
/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
retval = pci_set_mwi(pdev);
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 3ef2c0cdf1d..e9e5bc178ce 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)
msleep(10);
}
if (wait_time <= 0)
- printk(KERN_WARNING "%s %s: early BIOS handoff "
+ printk(KERN_WARNING "%s %s: BIOS handoff "
"failed (BIOS bug ?)\n",
pdev->dev.bus_id, "OHCI");
@@ -212,8 +212,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
{
int wait_time, delta;
void __iomem *base, *op_reg_base;
- u32 hcc_params, val, temp;
- u8 cap_length;
+ u32 hcc_params, val;
+ u8 offset, cap_length;
+ int count = 256/4;
if (!mmio_resource_enabled(pdev, 0))
return;
@@ -224,51 +225,80 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
cap_length = readb(base);
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 = (hcc_params >> 8) & 0xff;
- if (hcc_params) {
- pci_read_config_dword(pdev,
- hcc_params + EHCI_USBLEGSUP,
- &val);
- if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
- /*
- * Ok, BIOS is in smm mode, try to hand off...
+ offset = (hcc_params >> 8) & 0xff;
+ while (offset && count--) {
+ u32 cap;
+ int msec;
+
+ pci_read_config_dword(pdev, offset, &cap);
+ switch (cap & 0xff) {
+ 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,
+ offset + EHCI_USBLEGCTLSTS,
+ &val);
+ pci_write_config_dword(pdev,
+ offset + EHCI_USBLEGCTLSTS,
+ val | EHCI_USBLEGCTLSTS_SOOE);
+ }
+
+ /* always say Linux will own the hardware
+ * by setting EHCI_USBLEGSUP_OS.
*/
- pci_read_config_dword(pdev,
- hcc_params + EHCI_USBLEGCTLSTS,
- &temp);
- pci_write_config_dword(pdev,
- hcc_params + EHCI_USBLEGCTLSTS,
- temp | EHCI_USBLEGCTLSTS_SOOE);
- val |= EHCI_USBLEGSUP_OS;
- pci_write_config_dword(pdev,
- hcc_params + EHCI_USBLEGSUP,
- val);
+ pci_write_config_byte(pdev, offset + 3, 1);
- wait_time = 500;
- do {
+ /* if boot firmware now owns EHCI, spin till
+ * it hands it over.
+ */
+ msec = 5000;
+ while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
msleep(10);
- wait_time -= 10;
- pci_read_config_dword(pdev,
- hcc_params + EHCI_USBLEGSUP,
- &val);
- } while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
- if (!wait_time) {
- /*
- * well, possibly buggy BIOS...
+ msec -= 10;
+ pci_read_config_dword(pdev, offset, &cap);
+ }
+
+ if (cap & EHCI_USBLEGSUP_BIOS) {
+ /* well, possibly buggy BIOS... try to shut
+ * it down, and hope nothing goes too wrong
*/
- printk(KERN_WARNING "%s %s: early BIOS handoff "
+ printk(KERN_WARNING "%s %s: BIOS handoff "
"failed (BIOS bug ?)\n",
pdev->dev.bus_id, "EHCI");
- pci_write_config_dword(pdev,
- hcc_params + EHCI_USBLEGSUP,
- EHCI_USBLEGSUP_OS);
- pci_write_config_dword(pdev,
- hcc_params + EHCI_USBLEGCTLSTS,
- 0);
+ pci_write_config_byte(pdev, offset + 2, 0);
}
+
+ /* just in case, always disable EHCI SMIs */
+ pci_write_config_dword(pdev,
+ offset + EHCI_USBLEGCTLSTS,
+ 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