xref: /openbmc/linux/drivers/scsi/scsi_pm.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2db5bd1e0SAlan Stern /*
3db5bd1e0SAlan Stern  *	scsi_pm.c	Copyright (C) 2010 Alan Stern
4db5bd1e0SAlan Stern  *
5db5bd1e0SAlan Stern  *	SCSI dynamic Power Management
6db5bd1e0SAlan Stern  *		Initial version: Alan Stern <stern@rowland.harvard.edu>
7db5bd1e0SAlan Stern  */
8db5bd1e0SAlan Stern 
9db5bd1e0SAlan Stern #include <linux/pm_runtime.h>
1009703660SPaul Gortmaker #include <linux/export.h>
11bca6b067SBart Van Assche #include <linux/blk-pm.h>
12db5bd1e0SAlan Stern 
13db5bd1e0SAlan Stern #include <scsi/scsi.h>
14db5bd1e0SAlan Stern #include <scsi/scsi_device.h>
15db5bd1e0SAlan Stern #include <scsi/scsi_driver.h>
16db5bd1e0SAlan Stern #include <scsi/scsi_host.h>
17db5bd1e0SAlan Stern 
18db5bd1e0SAlan Stern #include "scsi_priv.h"
19db5bd1e0SAlan Stern 
206627b38fSAaron Lu #ifdef CONFIG_PM_SLEEP
216627b38fSAaron Lu 
do_scsi_suspend(struct device * dev,const struct dev_pm_ops * pm)223c31b52fSDan Williams static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
23db5bd1e0SAlan Stern {
243c31b52fSDan Williams 	return pm && pm->suspend ? pm->suspend(dev) : 0;
253c31b52fSDan Williams }
263c31b52fSDan Williams 
do_scsi_freeze(struct device * dev,const struct dev_pm_ops * pm)273c31b52fSDan Williams static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
283c31b52fSDan Williams {
293c31b52fSDan Williams 	return pm && pm->freeze ? pm->freeze(dev) : 0;
303c31b52fSDan Williams }
313c31b52fSDan Williams 
do_scsi_poweroff(struct device * dev,const struct dev_pm_ops * pm)323c31b52fSDan Williams static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
333c31b52fSDan Williams {
343c31b52fSDan Williams 	return pm && pm->poweroff ? pm->poweroff(dev) : 0;
353c31b52fSDan Williams }
363c31b52fSDan Williams 
do_scsi_resume(struct device * dev,const struct dev_pm_ops * pm)373c31b52fSDan Williams static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
383c31b52fSDan Williams {
393c31b52fSDan Williams 	return pm && pm->resume ? pm->resume(dev) : 0;
403c31b52fSDan Williams }
413c31b52fSDan Williams 
do_scsi_thaw(struct device * dev,const struct dev_pm_ops * pm)423c31b52fSDan Williams static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
433c31b52fSDan Williams {
443c31b52fSDan Williams 	return pm && pm->thaw ? pm->thaw(dev) : 0;
453c31b52fSDan Williams }
463c31b52fSDan Williams 
do_scsi_restore(struct device * dev,const struct dev_pm_ops * pm)473c31b52fSDan Williams static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
483c31b52fSDan Williams {
493c31b52fSDan Williams 	return pm && pm->restore ? pm->restore(dev) : 0;
503c31b52fSDan Williams }
513c31b52fSDan Williams 
scsi_dev_type_suspend(struct device * dev,int (* cb)(struct device *,const struct dev_pm_ops *))523c31b52fSDan Williams static int scsi_dev_type_suspend(struct device *dev,
533c31b52fSDan Williams 		int (*cb)(struct device *, const struct dev_pm_ops *))
543c31b52fSDan Williams {
553c31b52fSDan Williams 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
56db5bd1e0SAlan Stern 	int err;
57db5bd1e0SAlan Stern 
58db5bd1e0SAlan Stern 	err = scsi_device_quiesce(to_scsi_device(dev));
59db5bd1e0SAlan Stern 	if (err == 0) {
603c31b52fSDan Williams 		err = cb(dev, pm);
61d20ec597SAaron Lu 		if (err)
62d20ec597SAaron Lu 			scsi_device_resume(to_scsi_device(dev));
63d20ec597SAaron Lu 	}
64db5bd1e0SAlan Stern 	dev_dbg(dev, "scsi suspend: %d\n", err);
65db5bd1e0SAlan Stern 	return err;
66db5bd1e0SAlan Stern }
67db5bd1e0SAlan Stern 
6880d2fd48SAaron Lu static int
scsi_bus_suspend_common(struct device * dev,int (* cb)(struct device *,const struct dev_pm_ops *))693c31b52fSDan Williams scsi_bus_suspend_common(struct device *dev,
703c31b52fSDan Williams 		int (*cb)(struct device *, const struct dev_pm_ops *))
71db5bd1e0SAlan Stern {
729131bff6SBart Van Assche 	if (!scsi_is_sdev_device(dev))
7380d2fd48SAaron Lu 		return 0;
7428640516SLin Ming 
759131bff6SBart Van Assche 	return scsi_dev_type_suspend(dev, cb);
76db5bd1e0SAlan Stern }
77db5bd1e0SAlan Stern 
scsi_bus_resume_common(struct device * dev,int (* cb)(struct device *,const struct dev_pm_ops *))783c31b52fSDan Williams static int scsi_bus_resume_common(struct device *dev,
793c31b52fSDan Williams 		int (*cb)(struct device *, const struct dev_pm_ops *))
803c31b52fSDan Williams {
819131bff6SBart Van Assche 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
829131bff6SBart Van Assche 	int err;
839131bff6SBart Van Assche 
849131bff6SBart Van Assche 	if (!scsi_is_sdev_device(dev))
853c31b52fSDan Williams 		return 0;
869131bff6SBart Van Assche 
879131bff6SBart Van Assche 	err = cb(dev, pm);
889131bff6SBart Van Assche 	scsi_device_resume(to_scsi_device(dev));
899131bff6SBart Van Assche 	dev_dbg(dev, "scsi resume: %d\n", err);
909131bff6SBart Van Assche 
919131bff6SBart Van Assche 	return err;
92db5bd1e0SAlan Stern }
93db5bd1e0SAlan Stern 
scsi_bus_prepare(struct device * dev)94fea6d607SAlan Stern static int scsi_bus_prepare(struct device *dev)
95fea6d607SAlan Stern {
96f049cf1aSBart Van Assche 	if (scsi_is_host_device(dev)) {
97fea6d607SAlan Stern 		/* Wait until async scanning is finished */
98fea6d607SAlan Stern 		scsi_complete_async_scans();
99fea6d607SAlan Stern 	}
100fea6d607SAlan Stern 	return 0;
101fea6d607SAlan Stern }
102fea6d607SAlan Stern 
scsi_bus_suspend(struct device * dev)103db5bd1e0SAlan Stern static int scsi_bus_suspend(struct device *dev)
104db5bd1e0SAlan Stern {
1053c31b52fSDan Williams 	return scsi_bus_suspend_common(dev, do_scsi_suspend);
10680d2fd48SAaron Lu }
10780d2fd48SAaron Lu 
scsi_bus_resume(struct device * dev)10880d2fd48SAaron Lu static int scsi_bus_resume(struct device *dev)
10980d2fd48SAaron Lu {
1103c31b52fSDan Williams 	return scsi_bus_resume_common(dev, do_scsi_resume);
111db5bd1e0SAlan Stern }
112db5bd1e0SAlan Stern 
scsi_bus_freeze(struct device * dev)113db5bd1e0SAlan Stern static int scsi_bus_freeze(struct device *dev)
114db5bd1e0SAlan Stern {
1153c31b52fSDan Williams 	return scsi_bus_suspend_common(dev, do_scsi_freeze);
11680d2fd48SAaron Lu }
11780d2fd48SAaron Lu 
scsi_bus_thaw(struct device * dev)11880d2fd48SAaron Lu static int scsi_bus_thaw(struct device *dev)
11980d2fd48SAaron Lu {
1203c31b52fSDan Williams 	return scsi_bus_resume_common(dev, do_scsi_thaw);
121db5bd1e0SAlan Stern }
122db5bd1e0SAlan Stern 
scsi_bus_poweroff(struct device * dev)123db5bd1e0SAlan Stern static int scsi_bus_poweroff(struct device *dev)
124db5bd1e0SAlan Stern {
1253c31b52fSDan Williams 	return scsi_bus_suspend_common(dev, do_scsi_poweroff);
12680d2fd48SAaron Lu }
12780d2fd48SAaron Lu 
scsi_bus_restore(struct device * dev)12880d2fd48SAaron Lu static int scsi_bus_restore(struct device *dev)
12980d2fd48SAaron Lu {
1303c31b52fSDan Williams 	return scsi_bus_resume_common(dev, do_scsi_restore);
131db5bd1e0SAlan Stern }
132db5bd1e0SAlan Stern 
133db5bd1e0SAlan Stern #else /* CONFIG_PM_SLEEP */
134db5bd1e0SAlan Stern 
135fea6d607SAlan Stern #define scsi_bus_prepare		NULL
136db5bd1e0SAlan Stern #define scsi_bus_suspend		NULL
13780d2fd48SAaron Lu #define scsi_bus_resume			NULL
138db5bd1e0SAlan Stern #define scsi_bus_freeze			NULL
13980d2fd48SAaron Lu #define scsi_bus_thaw			NULL
140db5bd1e0SAlan Stern #define scsi_bus_poweroff		NULL
14180d2fd48SAaron Lu #define scsi_bus_restore		NULL
142db5bd1e0SAlan Stern 
143db5bd1e0SAlan Stern #endif /* CONFIG_PM_SLEEP */
144db5bd1e0SAlan Stern 
sdev_runtime_suspend(struct device * dev)1456627b38fSAaron Lu static int sdev_runtime_suspend(struct device *dev)
146bc4f2401SAlan Stern {
1476627b38fSAaron Lu 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
1486627b38fSAaron Lu 	struct scsi_device *sdev = to_scsi_device(dev);
14949718f0fSAlan Stern 	int err = 0;
150bc4f2401SAlan Stern 
1516df339a5SLin Ming 	err = blk_pre_runtime_suspend(sdev->request_queue);
1526df339a5SLin Ming 	if (err)
1536df339a5SLin Ming 		return err;
1541c69d3b6SKen Xue 	if (pm && pm->runtime_suspend)
1556627b38fSAaron Lu 		err = pm->runtime_suspend(dev);
1566df339a5SLin Ming 	blk_post_runtime_suspend(sdev->request_queue, err);
1571c69d3b6SKen Xue 
1586df339a5SLin Ming 	return err;
1596df339a5SLin Ming }
1606df339a5SLin Ming 
scsi_runtime_suspend(struct device * dev)1616df339a5SLin Ming static int scsi_runtime_suspend(struct device *dev)
1626df339a5SLin Ming {
1636df339a5SLin Ming 	int err = 0;
1646df339a5SLin Ming 
1656df339a5SLin Ming 	dev_dbg(dev, "scsi_runtime_suspend\n");
1666df339a5SLin Ming 	if (scsi_is_sdev_device(dev))
1676df339a5SLin Ming 		err = sdev_runtime_suspend(dev);
1686df339a5SLin Ming 
169bc4f2401SAlan Stern 	/* Insert hooks here for targets, hosts, and transport classes */
170bc4f2401SAlan Stern 
171bc4f2401SAlan Stern 	return err;
172bc4f2401SAlan Stern }
173bc4f2401SAlan Stern 
sdev_runtime_resume(struct device * dev)1746df339a5SLin Ming static int sdev_runtime_resume(struct device *dev)
1756df339a5SLin Ming {
1766df339a5SLin Ming 	struct scsi_device *sdev = to_scsi_device(dev);
1776df339a5SLin Ming 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
1786627b38fSAaron Lu 	int err = 0;
1796df339a5SLin Ming 
1806627b38fSAaron Lu 	blk_pre_runtime_resume(sdev->request_queue);
1811c69d3b6SKen Xue 	if (pm && pm->runtime_resume)
1826627b38fSAaron Lu 		err = pm->runtime_resume(dev);
183*6e1fcab0SAlan Stern 	blk_post_runtime_resume(sdev->request_queue);
1841c69d3b6SKen Xue 
1856627b38fSAaron Lu 	return err;
1866df339a5SLin Ming }
1876df339a5SLin Ming 
scsi_runtime_resume(struct device * dev)188bc4f2401SAlan Stern static int scsi_runtime_resume(struct device *dev)
189bc4f2401SAlan Stern {
190bc4f2401SAlan Stern 	int err = 0;
191bc4f2401SAlan Stern 
192bc4f2401SAlan Stern 	dev_dbg(dev, "scsi_runtime_resume\n");
193bc4f2401SAlan Stern 	if (scsi_is_sdev_device(dev))
1946df339a5SLin Ming 		err = sdev_runtime_resume(dev);
195bc4f2401SAlan Stern 
196bc4f2401SAlan Stern 	/* Insert hooks here for targets, hosts, and transport classes */
197bc4f2401SAlan Stern 
198bc4f2401SAlan Stern 	return err;
199bc4f2401SAlan Stern }
200bc4f2401SAlan Stern 
scsi_runtime_idle(struct device * dev)201bc4f2401SAlan Stern static int scsi_runtime_idle(struct device *dev)
202bc4f2401SAlan Stern {
203bc4f2401SAlan Stern 	dev_dbg(dev, "scsi_runtime_idle\n");
204bc4f2401SAlan Stern 
205bc4f2401SAlan Stern 	/* Insert hooks here for targets, hosts, and transport classes */
206bc4f2401SAlan Stern 
2076df339a5SLin Ming 	if (scsi_is_sdev_device(dev)) {
2086df339a5SLin Ming 		pm_runtime_mark_last_busy(dev);
20945f0a85cSRafael J. Wysocki 		pm_runtime_autosuspend(dev);
21045f0a85cSRafael J. Wysocki 		return -EBUSY;
2116df339a5SLin Ming 	}
2126627b38fSAaron Lu 
21345f0a85cSRafael J. Wysocki 	return 0;
214bc4f2401SAlan Stern }
215bc4f2401SAlan Stern 
scsi_autopm_get_device(struct scsi_device * sdev)216bc4f2401SAlan Stern int scsi_autopm_get_device(struct scsi_device *sdev)
217bc4f2401SAlan Stern {
218bc4f2401SAlan Stern 	int	err;
219bc4f2401SAlan Stern 
220bc4f2401SAlan Stern 	err = pm_runtime_get_sync(&sdev->sdev_gendev);
221632e270eSRafael J. Wysocki 	if (err < 0 && err !=-EACCES)
222bc4f2401SAlan Stern 		pm_runtime_put_sync(&sdev->sdev_gendev);
223632e270eSRafael J. Wysocki 	else
224bc4f2401SAlan Stern 		err = 0;
225bc4f2401SAlan Stern 	return err;
226bc4f2401SAlan Stern }
227bc4f2401SAlan Stern EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
228bc4f2401SAlan Stern 
scsi_autopm_put_device(struct scsi_device * sdev)229bc4f2401SAlan Stern void scsi_autopm_put_device(struct scsi_device *sdev)
230bc4f2401SAlan Stern {
231bc4f2401SAlan Stern 	pm_runtime_put_sync(&sdev->sdev_gendev);
232bc4f2401SAlan Stern }
233bc4f2401SAlan Stern EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
234bc4f2401SAlan Stern 
scsi_autopm_get_target(struct scsi_target * starget)235bc4f2401SAlan Stern void scsi_autopm_get_target(struct scsi_target *starget)
236bc4f2401SAlan Stern {
237bc4f2401SAlan Stern 	pm_runtime_get_sync(&starget->dev);
238bc4f2401SAlan Stern }
239bc4f2401SAlan Stern 
scsi_autopm_put_target(struct scsi_target * starget)240bc4f2401SAlan Stern void scsi_autopm_put_target(struct scsi_target *starget)
241bc4f2401SAlan Stern {
242bc4f2401SAlan Stern 	pm_runtime_put_sync(&starget->dev);
243bc4f2401SAlan Stern }
244bc4f2401SAlan Stern 
scsi_autopm_get_host(struct Scsi_Host * shost)245bc4f2401SAlan Stern int scsi_autopm_get_host(struct Scsi_Host *shost)
246bc4f2401SAlan Stern {
247bc4f2401SAlan Stern 	int	err;
248bc4f2401SAlan Stern 
249bc4f2401SAlan Stern 	err = pm_runtime_get_sync(&shost->shost_gendev);
250632e270eSRafael J. Wysocki 	if (err < 0 && err !=-EACCES)
251bc4f2401SAlan Stern 		pm_runtime_put_sync(&shost->shost_gendev);
252632e270eSRafael J. Wysocki 	else
253bc4f2401SAlan Stern 		err = 0;
254bc4f2401SAlan Stern 	return err;
255bc4f2401SAlan Stern }
256bc4f2401SAlan Stern 
scsi_autopm_put_host(struct Scsi_Host * shost)257bc4f2401SAlan Stern void scsi_autopm_put_host(struct Scsi_Host *shost)
258bc4f2401SAlan Stern {
259bc4f2401SAlan Stern 	pm_runtime_put_sync(&shost->shost_gendev);
260bc4f2401SAlan Stern }
261bc4f2401SAlan Stern 
262db5bd1e0SAlan Stern const struct dev_pm_ops scsi_bus_pm_ops = {
263fea6d607SAlan Stern 	.prepare =		scsi_bus_prepare,
264db5bd1e0SAlan Stern 	.suspend =		scsi_bus_suspend,
26580d2fd48SAaron Lu 	.resume =		scsi_bus_resume,
266db5bd1e0SAlan Stern 	.freeze =		scsi_bus_freeze,
26780d2fd48SAaron Lu 	.thaw =			scsi_bus_thaw,
268db5bd1e0SAlan Stern 	.poweroff =		scsi_bus_poweroff,
26980d2fd48SAaron Lu 	.restore =		scsi_bus_restore,
270bc4f2401SAlan Stern 	.runtime_suspend =	scsi_runtime_suspend,
271bc4f2401SAlan Stern 	.runtime_resume =	scsi_runtime_resume,
272bc4f2401SAlan Stern 	.runtime_idle =		scsi_runtime_idle,
273db5bd1e0SAlan Stern };
274