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

Commit 3da7cff4 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: add runtime PM for PCI-based host controllers



This patch (as1386) adds runtime-PM support for PCI-based USB host
controllers.  By default autosuspend is disallowed; the user must
enable it by writing "auto" to the controller's power/control sysfs
attribute.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0d436b42
Loading
Loading
Loading
Loading
+62 −14
Original line number Original line Diff line number Diff line
@@ -250,6 +250,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
	if (retval != 0)
	if (retval != 0)
		goto err4;
		goto err4;
	set_hs_companion(dev, hcd);
	set_hs_companion(dev, hcd);

	if (pci_dev_run_wake(dev))
		pm_runtime_put_noidle(&dev->dev);
	return retval;
	return retval;


 err4:
 err4:
@@ -292,6 +295,9 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
	if (!hcd)
	if (!hcd)
		return;
		return;


	if (pci_dev_run_wake(dev))
		pm_runtime_get_noresume(&dev->dev);

	/* Fake an interrupt request in order to give the driver a chance
	/* Fake an interrupt request in order to give the driver a chance
	 * to test whether the controller hardware has been removed (e.g.,
	 * to test whether the controller hardware has been removed (e.g.,
	 * cardbus physical eject).
	 * cardbus physical eject).
@@ -325,12 +331,13 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
	if (!hcd)
	if (!hcd)
		return;
		return;


	if (hcd->driver->shutdown)
	if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
			hcd->driver->shutdown)
		hcd->driver->shutdown(hcd);
		hcd->driver->shutdown(hcd);
}
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);


#ifdef	CONFIG_PM_SLEEP
#ifdef	CONFIG_PM_OPS


#ifdef	CONFIG_PPC_PMAC
#ifdef	CONFIG_PPC_PMAC
static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
@@ -366,7 +373,7 @@ static int check_root_hub_suspended(struct device *dev)
	return 0;
	return 0;
}
}


static int hcd_pci_suspend(struct device *dev)
static int suspend_common(struct device *dev, bool do_wakeup)
{
{
	struct pci_dev		*pci_dev = to_pci_dev(dev);
	struct pci_dev		*pci_dev = to_pci_dev(dev);
	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
@@ -381,13 +388,7 @@ static int hcd_pci_suspend(struct device *dev)
	if (retval)
	if (retval)
		return retval;
		return retval;


	/* We might already be suspended (runtime PM -- not yet written) */
	if (pci_dev->current_state != PCI_D0)
		return retval;

	if (hcd->driver->pci_suspend) {
	if (hcd->driver->pci_suspend) {
		bool	do_wakeup = device_may_wakeup(dev);

		/* Optimization: Don't suspend if a root-hub wakeup is
		/* Optimization: Don't suspend if a root-hub wakeup is
		 * pending and it would cause the HCD to wake up anyway.
		 * pending and it would cause the HCD to wake up anyway.
		 */
		 */
@@ -439,9 +440,7 @@ static int resume_common(struct device *dev, int event)
	clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
	clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);


	if (hcd->driver->pci_resume) {
	if (hcd->driver->pci_resume) {
		/* This call should be made only during system resume,
		if (event != PM_EVENT_AUTO_RESUME)
		 * not during runtime resume.
		 */
			wait_for_companions(pci_dev, hcd);
			wait_for_companions(pci_dev, hcd);


		retval = hcd->driver->pci_resume(hcd,
		retval = hcd->driver->pci_resume(hcd,
@@ -454,6 +453,13 @@ static int resume_common(struct device *dev, int event)
	return retval;
	return retval;
}
}


#ifdef	CONFIG_PM_SLEEP

static int hcd_pci_suspend(struct device *dev)
{
	return suspend_common(dev, device_may_wakeup(dev));
}

static int hcd_pci_suspend_noirq(struct device *dev)
static int hcd_pci_suspend_noirq(struct device *dev)
{
{
	struct pci_dev		*pci_dev = to_pci_dev(dev);
	struct pci_dev		*pci_dev = to_pci_dev(dev);
@@ -513,6 +519,46 @@ static int hcd_pci_restore(struct device *dev)
	return resume_common(dev, PM_EVENT_RESTORE);
	return resume_common(dev, PM_EVENT_RESTORE);
}
}


#else

#define hcd_pci_suspend		NULL
#define hcd_pci_suspend_noirq	NULL
#define hcd_pci_resume_noirq	NULL
#define hcd_pci_resume		NULL
#define hcd_pci_restore		NULL

#endif	/* CONFIG_PM_SLEEP */

#ifdef	CONFIG_PM_RUNTIME

static int hcd_pci_runtime_suspend(struct device *dev)
{
	int	retval;

	retval = suspend_common(dev, true);
	if (retval == 0)
		powermac_set_asic(to_pci_dev(dev), 0);
	dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
	return retval;
}

static int hcd_pci_runtime_resume(struct device *dev)
{
	int	retval;

	powermac_set_asic(to_pci_dev(dev), 1);
	retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
	dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
	return retval;
}

#else

#define hcd_pci_runtime_suspend	NULL
#define hcd_pci_runtime_resume	NULL

#endif	/* CONFIG_PM_RUNTIME */

const struct dev_pm_ops usb_hcd_pci_pm_ops = {
const struct dev_pm_ops usb_hcd_pci_pm_ops = {
	.suspend	= hcd_pci_suspend,
	.suspend	= hcd_pci_suspend,
	.suspend_noirq	= hcd_pci_suspend_noirq,
	.suspend_noirq	= hcd_pci_suspend_noirq,
@@ -526,7 +572,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
	.poweroff_noirq	= hcd_pci_suspend_noirq,
	.poweroff_noirq	= hcd_pci_suspend_noirq,
	.restore_noirq	= hcd_pci_resume_noirq,
	.restore_noirq	= hcd_pci_resume_noirq,
	.restore	= hcd_pci_restore,
	.restore	= hcd_pci_restore,
	.runtime_suspend = hcd_pci_runtime_suspend,
	.runtime_resume	= hcd_pci_runtime_resume,
};
};
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);


#endif	/* CONFIG_PM_SLEEP */
#endif	/* CONFIG_PM_OPS */