brintos

brintos / linux-shallow public Read only

0
0
Text · 6.0 KiB · d581613 Raw
274 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *	scsi_pm.c	Copyright (C) 2010 Alan Stern4 *5 *	SCSI dynamic Power Management6 *		Initial version: Alan Stern <stern@rowland.harvard.edu>7 */8 9#include <linux/pm_runtime.h>10#include <linux/export.h>11#include <linux/blk-pm.h>12 13#include <scsi/scsi.h>14#include <scsi/scsi_device.h>15#include <scsi/scsi_driver.h>16#include <scsi/scsi_host.h>17 18#include "scsi_priv.h"19 20#ifdef CONFIG_PM_SLEEP21 22static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)23{24	return pm && pm->suspend ? pm->suspend(dev) : 0;25}26 27static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)28{29	return pm && pm->freeze ? pm->freeze(dev) : 0;30}31 32static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)33{34	return pm && pm->poweroff ? pm->poweroff(dev) : 0;35}36 37static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)38{39	return pm && pm->resume ? pm->resume(dev) : 0;40}41 42static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)43{44	return pm && pm->thaw ? pm->thaw(dev) : 0;45}46 47static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)48{49	return pm && pm->restore ? pm->restore(dev) : 0;50}51 52static int scsi_dev_type_suspend(struct device *dev,53		int (*cb)(struct device *, const struct dev_pm_ops *))54{55	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;56	int err;57 58	err = scsi_device_quiesce(to_scsi_device(dev));59	if (err == 0) {60		err = cb(dev, pm);61		if (err)62			scsi_device_resume(to_scsi_device(dev));63	}64	dev_dbg(dev, "scsi suspend: %d\n", err);65	return err;66}67 68static int69scsi_bus_suspend_common(struct device *dev,70		int (*cb)(struct device *, const struct dev_pm_ops *))71{72	if (!scsi_is_sdev_device(dev))73		return 0;74 75	return scsi_dev_type_suspend(dev, cb);76}77 78static int scsi_bus_resume_common(struct device *dev,79		int (*cb)(struct device *, const struct dev_pm_ops *))80{81	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;82	int err;83 84	if (!scsi_is_sdev_device(dev))85		return 0;86 87	err = cb(dev, pm);88	scsi_device_resume(to_scsi_device(dev));89	dev_dbg(dev, "scsi resume: %d\n", err);90 91	return err;92}93 94static int scsi_bus_prepare(struct device *dev)95{96	if (scsi_is_host_device(dev)) {97		/* Wait until async scanning is finished */98		scsi_complete_async_scans();99	}100	return 0;101}102 103static int scsi_bus_suspend(struct device *dev)104{105	return scsi_bus_suspend_common(dev, do_scsi_suspend);106}107 108static int scsi_bus_resume(struct device *dev)109{110	return scsi_bus_resume_common(dev, do_scsi_resume);111}112 113static int scsi_bus_freeze(struct device *dev)114{115	return scsi_bus_suspend_common(dev, do_scsi_freeze);116}117 118static int scsi_bus_thaw(struct device *dev)119{120	return scsi_bus_resume_common(dev, do_scsi_thaw);121}122 123static int scsi_bus_poweroff(struct device *dev)124{125	return scsi_bus_suspend_common(dev, do_scsi_poweroff);126}127 128static int scsi_bus_restore(struct device *dev)129{130	return scsi_bus_resume_common(dev, do_scsi_restore);131}132 133#else /* CONFIG_PM_SLEEP */134 135#define scsi_bus_prepare		NULL136#define scsi_bus_suspend		NULL137#define scsi_bus_resume			NULL138#define scsi_bus_freeze			NULL139#define scsi_bus_thaw			NULL140#define scsi_bus_poweroff		NULL141#define scsi_bus_restore		NULL142 143#endif /* CONFIG_PM_SLEEP */144 145static int sdev_runtime_suspend(struct device *dev)146{147	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;148	struct scsi_device *sdev = to_scsi_device(dev);149	int err = 0;150 151	err = blk_pre_runtime_suspend(sdev->request_queue);152	if (err)153		return err;154	if (pm && pm->runtime_suspend)155		err = pm->runtime_suspend(dev);156	blk_post_runtime_suspend(sdev->request_queue, err);157 158	return err;159}160 161static int scsi_runtime_suspend(struct device *dev)162{163	int err = 0;164 165	dev_dbg(dev, "scsi_runtime_suspend\n");166	if (scsi_is_sdev_device(dev))167		err = sdev_runtime_suspend(dev);168 169	/* Insert hooks here for targets, hosts, and transport classes */170 171	return err;172}173 174static int sdev_runtime_resume(struct device *dev)175{176	struct scsi_device *sdev = to_scsi_device(dev);177	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;178	int err = 0;179 180	blk_pre_runtime_resume(sdev->request_queue);181	if (pm && pm->runtime_resume)182		err = pm->runtime_resume(dev);183	blk_post_runtime_resume(sdev->request_queue);184 185	return err;186}187 188static int scsi_runtime_resume(struct device *dev)189{190	int err = 0;191 192	dev_dbg(dev, "scsi_runtime_resume\n");193	if (scsi_is_sdev_device(dev))194		err = sdev_runtime_resume(dev);195 196	/* Insert hooks here for targets, hosts, and transport classes */197 198	return err;199}200 201static int scsi_runtime_idle(struct device *dev)202{203	dev_dbg(dev, "scsi_runtime_idle\n");204 205	/* Insert hooks here for targets, hosts, and transport classes */206 207	if (scsi_is_sdev_device(dev)) {208		pm_runtime_mark_last_busy(dev);209		pm_runtime_autosuspend(dev);210		return -EBUSY;211	}212 213	return 0;214}215 216int scsi_autopm_get_device(struct scsi_device *sdev)217{218	int	err;219 220	err = pm_runtime_get_sync(&sdev->sdev_gendev);221	if (err < 0 && err !=-EACCES)222		pm_runtime_put_sync(&sdev->sdev_gendev);223	else224		err = 0;225	return err;226}227EXPORT_SYMBOL_GPL(scsi_autopm_get_device);228 229void scsi_autopm_put_device(struct scsi_device *sdev)230{231	pm_runtime_put_sync(&sdev->sdev_gendev);232}233EXPORT_SYMBOL_GPL(scsi_autopm_put_device);234 235void scsi_autopm_get_target(struct scsi_target *starget)236{237	pm_runtime_get_sync(&starget->dev);238}239 240void scsi_autopm_put_target(struct scsi_target *starget)241{242	pm_runtime_put_sync(&starget->dev);243}244 245int scsi_autopm_get_host(struct Scsi_Host *shost)246{247	int	err;248 249	err = pm_runtime_get_sync(&shost->shost_gendev);250	if (err < 0 && err !=-EACCES)251		pm_runtime_put_sync(&shost->shost_gendev);252	else253		err = 0;254	return err;255}256 257void scsi_autopm_put_host(struct Scsi_Host *shost)258{259	pm_runtime_put_sync(&shost->shost_gendev);260}261 262const struct dev_pm_ops scsi_bus_pm_ops = {263	.prepare =		scsi_bus_prepare,264	.suspend =		scsi_bus_suspend,265	.resume =		scsi_bus_resume,266	.freeze =		scsi_bus_freeze,267	.thaw =			scsi_bus_thaw,268	.poweroff =		scsi_bus_poweroff,269	.restore =		scsi_bus_restore,270	.runtime_suspend =	scsi_runtime_suspend,271	.runtime_resume =	scsi_runtime_resume,272	.runtime_idle =		scsi_runtime_idle,273};274