1 /* 2 * scsi_pm.c Copyright (C) 2010 Alan Stern 3 * 4 * SCSI dynamic Power Management 5 * Initial version: Alan Stern <stern@rowland.harvard.edu> 6 */ 7 8 #include <linux/pm_runtime.h> 9 #include <linux/export.h> 10 #include <linux/async.h> 11 12 #include <scsi/scsi.h> 13 #include <scsi/scsi_device.h> 14 #include <scsi/scsi_driver.h> 15 #include <scsi/scsi_host.h> 16 17 #include "scsi_priv.h" 18 19 static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg) 20 { 21 struct device_driver *drv; 22 int err; 23 24 err = scsi_device_quiesce(to_scsi_device(dev)); 25 if (err == 0) { 26 drv = dev->driver; 27 if (drv && drv->suspend) { 28 err = drv->suspend(dev, msg); 29 if (err) 30 scsi_device_resume(to_scsi_device(dev)); 31 } 32 } 33 dev_dbg(dev, "scsi suspend: %d\n", err); 34 return err; 35 } 36 37 static int scsi_dev_type_resume(struct device *dev) 38 { 39 struct device_driver *drv; 40 int err = 0; 41 42 drv = dev->driver; 43 if (drv && drv->resume) 44 err = drv->resume(dev); 45 scsi_device_resume(to_scsi_device(dev)); 46 dev_dbg(dev, "scsi resume: %d\n", err); 47 return err; 48 } 49 50 #ifdef CONFIG_PM_SLEEP 51 52 static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg) 53 { 54 int err = 0; 55 56 if (scsi_is_sdev_device(dev)) { 57 /* 58 * sd is the only high-level SCSI driver to implement runtime 59 * PM, and sd treats runtime suspend, system suspend, and 60 * system hibernate identically (but not system freeze). 61 */ 62 if (pm_runtime_suspended(dev)) { 63 if (msg.event == PM_EVENT_SUSPEND || 64 msg.event == PM_EVENT_HIBERNATE) 65 return 0; /* already suspended */ 66 67 /* wake up device so that FREEZE will succeed */ 68 pm_runtime_resume(dev); 69 } 70 err = scsi_dev_type_suspend(dev, msg); 71 } 72 return err; 73 } 74 75 static int scsi_bus_resume_common(struct device *dev) 76 { 77 int err = 0; 78 79 /* 80 * Parent device may have runtime suspended as soon as 81 * it is woken up during the system resume. 82 * 83 * Resume it on behalf of child. 84 */ 85 pm_runtime_get_sync(dev->parent); 86 87 if (scsi_is_sdev_device(dev)) 88 err = scsi_dev_type_resume(dev); 89 if (err == 0) { 90 pm_runtime_disable(dev); 91 pm_runtime_set_active(dev); 92 pm_runtime_enable(dev); 93 } 94 95 pm_runtime_put_sync(dev->parent); 96 97 return err; 98 } 99 100 static int scsi_bus_prepare(struct device *dev) 101 { 102 if (scsi_is_sdev_device(dev)) { 103 /* sd probing uses async_schedule. Wait until it finishes. */ 104 async_synchronize_full_domain(&scsi_sd_probe_domain); 105 106 } else if (scsi_is_host_device(dev)) { 107 /* Wait until async scanning is finished */ 108 scsi_complete_async_scans(); 109 } 110 return 0; 111 } 112 113 static int scsi_bus_suspend(struct device *dev) 114 { 115 return scsi_bus_suspend_common(dev, PMSG_SUSPEND); 116 } 117 118 static int scsi_bus_freeze(struct device *dev) 119 { 120 return scsi_bus_suspend_common(dev, PMSG_FREEZE); 121 } 122 123 static int scsi_bus_poweroff(struct device *dev) 124 { 125 return scsi_bus_suspend_common(dev, PMSG_HIBERNATE); 126 } 127 128 #else /* CONFIG_PM_SLEEP */ 129 130 #define scsi_bus_resume_common NULL 131 #define scsi_bus_prepare NULL 132 #define scsi_bus_suspend NULL 133 #define scsi_bus_freeze NULL 134 #define scsi_bus_poweroff NULL 135 136 #endif /* CONFIG_PM_SLEEP */ 137 138 #ifdef CONFIG_PM_RUNTIME 139 140 static int scsi_runtime_suspend(struct device *dev) 141 { 142 int err = 0; 143 144 dev_dbg(dev, "scsi_runtime_suspend\n"); 145 if (scsi_is_sdev_device(dev)) { 146 err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND); 147 if (err == -EAGAIN) 148 pm_schedule_suspend(dev, jiffies_to_msecs( 149 round_jiffies_up_relative(HZ/10))); 150 } 151 152 /* Insert hooks here for targets, hosts, and transport classes */ 153 154 return err; 155 } 156 157 static int scsi_runtime_resume(struct device *dev) 158 { 159 int err = 0; 160 161 dev_dbg(dev, "scsi_runtime_resume\n"); 162 if (scsi_is_sdev_device(dev)) 163 err = scsi_dev_type_resume(dev); 164 165 /* Insert hooks here for targets, hosts, and transport classes */ 166 167 return err; 168 } 169 170 static int scsi_runtime_idle(struct device *dev) 171 { 172 int err; 173 174 dev_dbg(dev, "scsi_runtime_idle\n"); 175 176 /* Insert hooks here for targets, hosts, and transport classes */ 177 178 if (scsi_is_sdev_device(dev)) 179 err = pm_schedule_suspend(dev, 100); 180 else 181 err = pm_runtime_suspend(dev); 182 return err; 183 } 184 185 int scsi_autopm_get_device(struct scsi_device *sdev) 186 { 187 int err; 188 189 err = pm_runtime_get_sync(&sdev->sdev_gendev); 190 if (err < 0 && err !=-EACCES) 191 pm_runtime_put_sync(&sdev->sdev_gendev); 192 else 193 err = 0; 194 return err; 195 } 196 EXPORT_SYMBOL_GPL(scsi_autopm_get_device); 197 198 void scsi_autopm_put_device(struct scsi_device *sdev) 199 { 200 pm_runtime_put_sync(&sdev->sdev_gendev); 201 } 202 EXPORT_SYMBOL_GPL(scsi_autopm_put_device); 203 204 void scsi_autopm_get_target(struct scsi_target *starget) 205 { 206 pm_runtime_get_sync(&starget->dev); 207 } 208 209 void scsi_autopm_put_target(struct scsi_target *starget) 210 { 211 pm_runtime_put_sync(&starget->dev); 212 } 213 214 int scsi_autopm_get_host(struct Scsi_Host *shost) 215 { 216 int err; 217 218 err = pm_runtime_get_sync(&shost->shost_gendev); 219 if (err < 0 && err !=-EACCES) 220 pm_runtime_put_sync(&shost->shost_gendev); 221 else 222 err = 0; 223 return err; 224 } 225 226 void scsi_autopm_put_host(struct Scsi_Host *shost) 227 { 228 pm_runtime_put_sync(&shost->shost_gendev); 229 } 230 231 #else 232 233 #define scsi_runtime_suspend NULL 234 #define scsi_runtime_resume NULL 235 #define scsi_runtime_idle NULL 236 237 #endif /* CONFIG_PM_RUNTIME */ 238 239 const struct dev_pm_ops scsi_bus_pm_ops = { 240 .prepare = scsi_bus_prepare, 241 .suspend = scsi_bus_suspend, 242 .resume = scsi_bus_resume_common, 243 .freeze = scsi_bus_freeze, 244 .thaw = scsi_bus_resume_common, 245 .poweroff = scsi_bus_poweroff, 246 .restore = scsi_bus_resume_common, 247 .runtime_suspend = scsi_runtime_suspend, 248 .runtime_resume = scsi_runtime_resume, 249 .runtime_idle = scsi_runtime_idle, 250 }; 251