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