Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+3 −60
Original line number Original line Diff line number Diff line
@@ -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)
		}
		}
	}
	}


	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);
+68 −38
Original line number Original line Diff line number Diff line
@@ -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)
{
{
	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)


	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