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

Commit bbb44d9f authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Jesse Barnes
Browse files

PCI: implement new suspend/resume callbacks



Implement new suspend and hibernation callbacks for the PCI bus type.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 1eede070
Loading
Loading
Loading
Loading
+346 −46
Original line number Diff line number Diff line
@@ -274,16 +274,25 @@ static int pci_device_remove(struct device * dev)
	return 0;
}

static int pci_device_suspend(struct device * dev, pm_message_t state)
static void pci_device_shutdown(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int i = 0;

	if (drv && drv->suspend) {
		i = drv->suspend(pci_dev, state);
		suspend_report_result(drv->suspend, i);
	} else {
	if (drv && drv->shutdown)
		drv->shutdown(pci_dev);
	pci_msi_shutdown(pci_dev);
	pci_msix_shutdown(pci_dev);
}

#ifdef CONFIG_PM_SLEEP

/*
 * Default "suspend" method for devices that have no driver provided suspend,
 * or not even a driver at all.
 */
static void pci_default_pm_suspend(struct pci_dev *pci_dev)
{
	pci_save_state(pci_dev);
	/*
	 * mark its power state as "unknown", since we don't know if
@@ -293,12 +302,44 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
		pci_dev->current_state = PCI_UNKNOWN;
}

	pci_fixup_device(pci_fixup_suspend, pci_dev);
/*
 * Default "resume" method for devices that have no driver provided resume,
 * or not even a driver at all.
 */
static int pci_default_pm_resume(struct pci_dev *pci_dev)
{
	int retval = 0;

	/* restore the PCI config space */
	pci_restore_state(pci_dev);
	/* if the device was enabled before suspend, reenable */
	retval = pci_reenable_device(pci_dev);
	/*
	 * if the device was busmaster before the suspend, make it busmaster
	 * again
	 */
	if (pci_dev->is_busmaster)
		pci_set_master(pci_dev);

	return retval;
}

static int pci_legacy_suspend(struct device *dev, pm_message_t state)
{
	struct pci_dev * pci_dev = to_pci_dev(dev);
	struct pci_driver * drv = pci_dev->driver;
	int i = 0;

	if (drv && drv->suspend) {
		i = drv->suspend(pci_dev, state);
		suspend_report_result(drv->suspend, i);
	} else {
		pci_default_pm_suspend(pci_dev);
	}
	return i;
}

static int pci_device_suspend_late(struct device * dev, pm_message_t state)
static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
{
	struct pci_dev * pci_dev = to_pci_dev(dev);
	struct pci_driver * drv = pci_dev->driver;
@@ -311,63 +352,322 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state)
	return i;
}

/*
 * Default resume method for devices that have no driver provided resume,
 * or not even a driver at all.
 */
static int pci_default_resume(struct pci_dev *pci_dev)
static int pci_legacy_resume(struct device *dev)
{
	int retval = 0;
	int error;
	struct pci_dev * pci_dev = to_pci_dev(dev);
	struct pci_driver * drv = pci_dev->driver;

	/* restore the PCI config space */
	pci_restore_state(pci_dev);
	/* if the device was enabled before suspend, reenable */
	retval = pci_reenable_device(pci_dev);
	/* if the device was busmaster before the suspend, make it busmaster again */
	if (pci_dev->is_busmaster)
		pci_set_master(pci_dev);
	if (drv && drv->resume)
		error = drv->resume(pci_dev);
	else
		error = pci_default_pm_resume(pci_dev);
	return error;
}

	return retval;
static int pci_legacy_resume_early(struct device *dev)
{
	int error = 0;
	struct pci_dev * pci_dev = to_pci_dev(dev);
	struct pci_driver * drv = pci_dev->driver;

	if (drv && drv->resume_early)
		error = drv->resume_early(pci_dev);
	return error;
}

static int pci_device_resume(struct device * dev)
static int pci_pm_prepare(struct device *dev)
{
	struct device_driver *drv = dev->driver;
	int error = 0;

	if (drv && drv->pm && drv->pm->prepare)
		error = drv->pm->prepare(dev);

	return error;
}

static void pci_pm_complete(struct device *dev)
{
	struct device_driver *drv = dev->driver;

	if (drv && drv->pm && drv->pm->complete)
		drv->pm->complete(dev);
}

#ifdef CONFIG_SUSPEND

static int pci_pm_suspend(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct device_driver *drv = dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->suspend) {
			error = drv->pm->suspend(dev);
			suspend_report_result(drv->pm->suspend, error);
		} else {
			pci_default_pm_suspend(pci_dev);
		}
	} else {
		error = pci_legacy_suspend(dev, PMSG_SUSPEND);
	}
	pci_fixup_device(pci_fixup_suspend, pci_dev);

	return error;
}

static int pci_pm_suspend_noirq(struct device *dev)
{
	int error;
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->suspend_noirq) {
			error = drv->pm->suspend_noirq(dev);
			suspend_report_result(drv->pm->suspend_noirq, error);
		}
	} else {
		error = pci_legacy_suspend_late(dev, PMSG_SUSPEND);
	}

	return error;
}

static int pci_pm_resume(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct device_driver *drv = dev->driver;
	int error;

	if (drv && drv->resume)
		error = drv->resume(pci_dev);
	else
		error = pci_default_resume(pci_dev);
	pci_fixup_device(pci_fixup_resume, pci_dev);

	if (drv && drv->pm) {
		error = drv->pm->resume ? drv->pm->resume(dev) :
			pci_default_pm_resume(pci_dev);
	} else {
		error = pci_legacy_resume(dev);
	}

	return error;
}

static int pci_device_resume_early(struct device * dev)
static int pci_pm_resume_noirq(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	pci_fixup_device(pci_fixup_resume_early, pci_dev);

	if (drv && drv->pm) {
		if (drv->pm->resume_noirq)
			error = drv->pm->resume_noirq(dev);
	} else {
		error = pci_legacy_resume_early(dev);
	}

	return error;
}

#else /* !CONFIG_SUSPEND */

#define pci_pm_suspend		NULL
#define pci_pm_suspend_noirq	NULL
#define pci_pm_resume		NULL
#define pci_pm_resume_noirq	NULL

#endif /* !CONFIG_SUSPEND */

#ifdef CONFIG_HIBERNATION

static int pci_pm_freeze(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct device_driver *drv = dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->freeze) {
			error = drv->pm->freeze(dev);
			suspend_report_result(drv->pm->freeze, error);
		} else {
			pci_default_pm_suspend(pci_dev);
		}
	} else {
		error = pci_legacy_suspend(dev, PMSG_FREEZE);
		pci_fixup_device(pci_fixup_suspend, pci_dev);
	}

	return error;
}

static int pci_pm_freeze_noirq(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->freeze_noirq) {
			error = drv->pm->freeze_noirq(dev);
			suspend_report_result(drv->pm->freeze_noirq, error);
		}
	} else {
		error = pci_legacy_suspend_late(dev, PMSG_FREEZE);
	}

	return error;
}

static int pci_pm_thaw(struct device *dev)
{
	struct device_driver *drv = dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->thaw)
			error =  drv->pm->thaw(dev);
	} else {
		pci_fixup_device(pci_fixup_resume, to_pci_dev(dev));
		error = pci_legacy_resume(dev);
	}

	return error;
}

static int pci_pm_thaw_noirq(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	if (drv && drv->pm) {
		if (drv->pm->thaw_noirq)
			error = drv->pm->thaw_noirq(dev);
	} else {
		pci_fixup_device(pci_fixup_resume_early, pci_dev);
		error = pci_legacy_resume_early(dev);
	}

	if (drv && drv->resume_early)
		error = drv->resume_early(pci_dev);
	return error;
}

static void pci_device_shutdown(struct device *dev)
static int pci_pm_poweroff(struct device *dev)
{
	struct device_driver *drv = dev->driver;
	int error = 0;

	pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));

	if (drv && drv->pm) {
		if (drv->pm->poweroff) {
			error = drv->pm->poweroff(dev);
			suspend_report_result(drv->pm->poweroff, error);
		}
	} else {
		error = pci_legacy_suspend(dev, PMSG_HIBERNATE);
	}

	return error;
}

static int pci_pm_poweroff_noirq(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	if (drv && drv->shutdown)
		drv->shutdown(pci_dev);
	pci_msi_shutdown(pci_dev);
	pci_msix_shutdown(pci_dev);
	if (drv && drv->pm) {
		if (drv->pm->poweroff_noirq) {
			error = drv->pm->poweroff_noirq(dev);
			suspend_report_result(drv->pm->poweroff_noirq, error);
		}
	} else {
		error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
	}

	return error;
}

static int pci_pm_restore(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct device_driver *drv = dev->driver;
	int error;

	if (drv && drv->pm) {
		error = drv->pm->restore ? drv->pm->restore(dev) :
			pci_default_pm_resume(pci_dev);
	} else {
		error = pci_legacy_resume(dev);
	}
	pci_fixup_device(pci_fixup_resume, pci_dev);

	return error;
}

static int pci_pm_restore_noirq(struct device *dev)
{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct pci_driver *drv = pci_dev->driver;
	int error = 0;

	pci_fixup_device(pci_fixup_resume, pci_dev);

	if (drv && drv->pm) {
		if (drv->pm->restore_noirq)
			error = drv->pm->restore_noirq(dev);
	} else {
		error = pci_legacy_resume_early(dev);
	}
	pci_fixup_device(pci_fixup_resume_early, pci_dev);

	return error;
}

#else /* !CONFIG_HIBERNATION */

#define pci_pm_freeze		NULL
#define pci_pm_freeze_noirq	NULL
#define pci_pm_thaw		NULL
#define pci_pm_thaw_noirq	NULL
#define pci_pm_poweroff		NULL
#define pci_pm_poweroff_noirq	NULL
#define pci_pm_restore		NULL
#define pci_pm_restore_noirq	NULL

#endif /* !CONFIG_HIBERNATION */

struct pm_ext_ops pci_pm_ops = {
	.base = {
		.prepare = pci_pm_prepare,
		.complete = pci_pm_complete,
		.suspend = pci_pm_suspend,
		.resume = pci_pm_resume,
		.freeze = pci_pm_freeze,
		.thaw = pci_pm_thaw,
		.poweroff = pci_pm_poweroff,
		.restore = pci_pm_restore,
	},
	.suspend_noirq = pci_pm_suspend_noirq,
	.resume_noirq = pci_pm_resume_noirq,
	.freeze_noirq = pci_pm_freeze_noirq,
	.thaw_noirq = pci_pm_thaw_noirq,
	.poweroff_noirq = pci_pm_poweroff_noirq,
	.restore_noirq = pci_pm_restore_noirq,
};

#define PCI_PM_OPS_PTR	&pci_pm_ops

#else /* !CONFIG_PM_SLEEP */

#define PCI_PM_OPS_PTR	NULL

#endif /* !CONFIG_PM_SLEEP */

/**
 * __pci_register_driver - register a new pci driver
 * @drv: the driver structure to register
@@ -390,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
	drv->driver.owner = owner;
	drv->driver.mod_name = mod_name;

	if (drv->pm)
		drv->driver.pm = &drv->pm->base;

	spin_lock_init(&drv->dynids.lock);
	INIT_LIST_HEAD(&drv->dynids.list);

@@ -515,12 +818,9 @@ struct bus_type pci_bus_type = {
	.uevent		= pci_uevent,
	.probe		= pci_device_probe,
	.remove		= pci_device_remove,
	.suspend	= pci_device_suspend,
	.suspend_late	= pci_device_suspend_late,
	.resume_early	= pci_device_resume_early,
	.resume		= pci_device_resume,
	.shutdown	= pci_device_shutdown,
	.dev_attrs	= pci_dev_attrs,
	.pm		= PCI_PM_OPS_PTR,
};

static int __init pci_driver_init(void)
+1 −1
Original line number Diff line number Diff line
@@ -389,7 +389,7 @@ struct pci_driver {
	int  (*resume_early) (struct pci_dev *dev);
	int  (*resume) (struct pci_dev *dev);	                /* Device woken up */
	void (*shutdown) (struct pci_dev *dev);

	struct pm_ext_ops *pm;
	struct pci_error_handlers *err_handler;
	struct device_driver	driver;
	struct pci_dynids dynids;