Ejemplo n.º 1
0
static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
	struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
	int rc = 0;

	dev_dbg(uhci_dev(uhci), "%s\n", __func__);

	spin_lock_irq(&uhci->lock);
	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
		goto done_okay;		/* Already suspended or dead */

	/* All PCI host controllers are required to disable IRQ generation
	 * at the source, so we must turn off PIRQ.
	 */
	pci_write_config_word(pdev, USBLEGSUP, 0);
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);

	/* Enable platform-specific non-PME# wakeup */
	if (do_wakeup) {
		if (pdev->vendor == PCI_VENDOR_ID_INTEL)
			pci_write_config_byte(pdev, USBRES_INTEL,
					USBPORT1EN | USBPORT2EN);
	}

done_okay:
	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
	spin_unlock_irq(&uhci->lock);

	synchronize_irq(hcd->irq);

	/* Check for race with a wakeup request */
	if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
		uhci_pci_resume(hcd, false);
		rc = -EBUSY;
	}
	return rc;
}
/* dwc_hcd_suspend_common and dwc_hcd_resume_common are refer to
 * suspend_common and resume_common in usb core.
 * Because the usb core function just support PCI device.
 * So re-write them in here to support platform devices.
 */
static int dwc_hcd_suspend_common(struct device *dev)
{
	struct platform_device		*pdev = to_platform_device(dev);
	struct usb_hcd		*hcd = platform_get_drvdata(pdev);
	struct xhci_hcd		*xhci = hcd_to_xhci(hcd);
	int			retval = 0;
	u32 data = 0;

	if (!xhci) {
		dev_dbg(dev, "%s: host already stop!\n", __func__);
		return 0;
	}

	/* Root hub suspend should have stopped all downstream traffic,
	 * and all bus master traffic.  And done so for both the interface
	 * and the stub usb_device (which we check here).  But maybe it
	 * didn't; writing sysfs power/state files ignores such rules...
	 */
	if (HCD_RH_RUNNING(hcd)) {
		dev_warn(dev, "Root hub is not suspended\n");
		return -EBUSY;
	}
	if (hcd->shared_hcd) {
		hcd = hcd->shared_hcd;
		if (HCD_RH_RUNNING(hcd)) {
			dev_warn(dev, "Secondary root hub is not suspended\n");
			return -EBUSY;
		}
	}

	if (!HCD_DEAD(hcd)) {
		/* Optimization: Don't suspend if a root-hub wakeup is
		 * pending and it would cause the HCD to wake up anyway.
		 */
		if (HCD_WAKEUP_PENDING(hcd))
			return -EBUSY;
		if (hcd->shared_hcd &&
				HCD_WAKEUP_PENDING(hcd->shared_hcd))
			return -EBUSY;
		if (hcd->state != HC_STATE_SUSPENDED ||
				xhci->shared_hcd->state != HC_STATE_SUSPENDED)
			retval = -EINVAL;

		if (!retval) {
			/* The auto-resume is diabled by default. Need enable it
			 * if there have valid connection. To ensure that when
			 * device resumes, host does resume reflect within
			 * 900 usec as in USB spec.
			 */
			if (if_usb_devices_connected(xhci) == 1)
				dwc_xhci_enable_phy_auto_resume(
						xhci->main_hcd, true);

			/* Ensure that suspend enable are set for
			 * USB2 and USB3 PHY
			 */
			dwc_xhci_enable_phy_suspend(hcd, true);

			data = readl(hcd->regs + GCTL);
			data |= GCTL_GBL_HIBERNATION_EN;
			writel(data, hcd->regs + GCTL);
			dev_dbg(hcd->self.controller, "set xhci hibernation enable!\n");
			retval = xhci_suspend(xhci);
		}

		/* Check again in case wakeup raced with pci_suspend */
		if ((retval == 0 && HCD_WAKEUP_PENDING(hcd)) ||
				(retval == 0 && hcd->shared_hcd &&
				 HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
			xhci_resume(xhci, false);
			retval = -EBUSY;
		}
		if (retval)
			return retval;
	}

	synchronize_irq(dwc3_xhci.otg_irqnum);

	return retval;

}