diff options
| author | Lin Ming <ming.m.lin@intel.com> | 2011-12-04 20:20:27 -0500 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@redhat.com> | 2012-01-08 19:14:58 -0500 |
| commit | 5ef41082912bdfcb33fa53b8dba2ad17dea2ef90 (patch) | |
| tree | b63aff744994b75af6c3e5e5e70e01312027992e | |
| parent | 54f57588463db1105f4a93b2902a6f95cb8f796a (diff) | |
ata: add ata port system PM callbacks
Change ata_host_request_pm to ata_port_request_pm which performs
port suspend/resume.
Add ata port type driver which implements port PM callbacks.
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
| -rw-r--r-- | drivers/ata/libata-core.c | 144 | ||||
| -rw-r--r-- | drivers/ata/libata-transport.c | 1 | ||||
| -rw-r--r-- | drivers/ata/libata.h | 1 |
3 files changed, 76 insertions, 70 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c04ad68cb602..04c208e3cec6 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
| @@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link) | |||
| 5234 | } | 5234 | } |
| 5235 | 5235 | ||
| 5236 | #ifdef CONFIG_PM | 5236 | #ifdef CONFIG_PM |
| 5237 | static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg, | 5237 | static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, |
| 5238 | unsigned int action, unsigned int ehi_flags, | 5238 | unsigned int action, unsigned int ehi_flags, |
| 5239 | int wait) | 5239 | int wait) |
| 5240 | { | 5240 | { |
| 5241 | struct ata_link *link; | ||
| 5241 | unsigned long flags; | 5242 | unsigned long flags; |
| 5242 | int i, rc; | 5243 | int rc; |
| 5243 | |||
| 5244 | for (i = 0; i < host->n_ports; i++) { | ||
| 5245 | struct ata_port *ap = host->ports[i]; | ||
| 5246 | struct ata_link *link; | ||
| 5247 | 5244 | ||
| 5248 | /* Previous resume operation might still be in | 5245 | /* Previous resume operation might still be in |
| 5249 | * progress. Wait for PM_PENDING to clear. | 5246 | * progress. Wait for PM_PENDING to clear. |
| 5250 | */ | 5247 | */ |
| 5251 | if (ap->pflags & ATA_PFLAG_PM_PENDING) { | 5248 | if (ap->pflags & ATA_PFLAG_PM_PENDING) { |
| 5252 | ata_port_wait_eh(ap); | 5249 | ata_port_wait_eh(ap); |
| 5253 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); | 5250 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); |
| 5254 | } | 5251 | } |
| 5255 | 5252 | ||
| 5256 | /* request PM ops to EH */ | 5253 | /* request PM ops to EH */ |
| 5257 | spin_lock_irqsave(ap->lock, flags); | 5254 | spin_lock_irqsave(ap->lock, flags); |
| 5258 | 5255 | ||
| 5259 | ap->pm_mesg = mesg; | 5256 | ap->pm_mesg = mesg; |
| 5260 | if (wait) { | 5257 | if (wait) { |
| 5261 | rc = 0; | 5258 | rc = 0; |
| 5262 | ap->pm_result = &rc; | 5259 | ap->pm_result = &rc; |
| 5263 | } | 5260 | } |
| 5264 | 5261 | ||
| 5265 | ap->pflags |= ATA_PFLAG_PM_PENDING; | 5262 | ap->pflags |= ATA_PFLAG_PM_PENDING; |
| 5266 | ata_for_each_link(link, ap, HOST_FIRST) { | 5263 | ata_for_each_link(link, ap, HOST_FIRST) { |
| 5267 | link->eh_info.action |= action; | 5264 | link->eh_info.action |= action; |
| 5268 | link->eh_info.flags |= ehi_flags; | 5265 | link->eh_info.flags |= ehi_flags; |
| 5269 | } | 5266 | } |
| 5270 | 5267 | ||
| 5271 | ata_port_schedule_eh(ap); | 5268 | ata_port_schedule_eh(ap); |
| 5272 | 5269 | ||
| 5273 | spin_unlock_irqrestore(ap->lock, flags); | 5270 | spin_unlock_irqrestore(ap->lock, flags); |
| 5274 | 5271 | ||
| 5275 | /* wait and check result */ | 5272 | /* wait and check result */ |
| 5276 | if (wait) { | 5273 | if (wait) { |
| 5277 | ata_port_wait_eh(ap); | 5274 | ata_port_wait_eh(ap); |
| 5278 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); | 5275 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); |
| 5279 | if (rc) | ||
| 5280 | return rc; | ||
| 5281 | } | ||
| 5282 | } | 5276 | } |
| 5283 | 5277 | ||
| 5284 | return 0; | 5278 | return rc; |
| 5285 | } | 5279 | } |
| 5286 | 5280 | ||
| 5281 | #define to_ata_port(d) container_of(d, struct ata_port, tdev) | ||
| 5282 | |||
| 5283 | static int ata_port_suspend_common(struct device *dev) | ||
| 5284 | { | ||
| 5285 | struct ata_port *ap = to_ata_port(dev); | ||
| 5286 | int rc; | ||
| 5287 | |||
| 5288 | rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1); | ||
| 5289 | return rc; | ||
| 5290 | } | ||
| 5291 | |||
| 5292 | static int ata_port_suspend(struct device *dev) | ||
| 5293 | { | ||
| 5294 | if (pm_runtime_suspended(dev)) | ||
| 5295 | return 0; | ||
| 5296 | |||
| 5297 | return ata_port_suspend_common(dev); | ||
| 5298 | } | ||
| 5299 | |||
| 5300 | static int ata_port_resume(struct device *dev) | ||
| 5301 | { | ||
| 5302 | struct ata_port *ap = to_ata_port(dev); | ||
| 5303 | int rc; | ||
| 5304 | |||
| 5305 | rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, | ||
| 5306 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1); | ||
| 5307 | return rc; | ||
| 5308 | } | ||
| 5309 | |||
| 5310 | static const struct dev_pm_ops ata_port_pm_ops = { | ||
| 5311 | .suspend = ata_port_suspend, | ||
| 5312 | .resume = ata_port_resume, | ||
| 5313 | }; | ||
| 5314 | |||
| 5287 | /** | 5315 | /** |
| 5288 | * ata_host_suspend - suspend host | 5316 | * ata_host_suspend - suspend host |
| 5289 | * @host: host to suspend | 5317 | * @host: host to suspend |
| 5290 | * @mesg: PM message | 5318 | * @mesg: PM message |
| 5291 | * | 5319 | * |
| 5292 | * Suspend @host. Actual operation is performed by EH. This | 5320 | * Suspend @host. Actual operation is performed by port suspend. |
| 5293 | * function requests EH to perform PM operations and waits for EH | ||
| 5294 | * to finish. | ||
| 5295 | * | ||
| 5296 | * LOCKING: | ||
| 5297 | * Kernel thread context (may sleep). | ||
| 5298 | * | ||
| 5299 | * RETURNS: | ||
| 5300 | * 0 on success, -errno on failure. | ||
| 5301 | */ | 5321 | */ |
| 5302 | int ata_host_suspend(struct ata_host *host, pm_message_t mesg) | 5322 | int ata_host_suspend(struct ata_host *host, pm_message_t mesg) |
| 5303 | { | 5323 | { |
| 5304 | unsigned int ehi_flags = ATA_EHI_QUIET; | 5324 | host->dev->power.power_state = mesg; |
| 5305 | int rc; | 5325 | return 0; |
| 5306 | |||
| 5307 | /* | ||
| 5308 | * On some hardware, device fails to respond after spun down | ||
| 5309 | * for suspend. As the device won't be used before being | ||
| 5310 | * resumed, we don't need to touch the device. Ask EH to skip | ||
| 5311 | * the usual stuff and proceed directly to suspend. | ||
| 5312 | * | ||
| 5313 | * http://thread.gmane.org/gmane.linux.ide/46764 | ||
| 5314 | */ | ||
| 5315 | if (mesg.event == PM_EVENT_SUSPEND) | ||
| 5316 | ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY; | ||
| 5317 | |||
| 5318 | rc = ata_host_request_pm(host, mesg, 0, ehi_flags, 1); | ||
| 5319 | if (rc == 0) | ||
| 5320 | host->dev->power.power_state = mesg; | ||
| 5321 | return rc; | ||
| 5322 | } | 5326 | } |
| 5323 | 5327 | ||
| 5324 | /** | 5328 | /** |
| 5325 | * ata_host_resume - resume host | 5329 | * ata_host_resume - resume host |
| 5326 | * @host: host to resume | 5330 | * @host: host to resume |
| 5327 | * | 5331 | * |
| 5328 | * Resume @host. Actual operation is performed by EH. This | 5332 | * Resume @host. Actual operation is performed by port resume. |
| 5329 | * function requests EH to perform PM operations and returns. | ||
| 5330 | * Note that all resume operations are performed parallelly. | ||
| 5331 | * | ||
| 5332 | * LOCKING: | ||
| 5333 | * Kernel thread context (may sleep). | ||
| 5334 | */ | 5333 | */ |
| 5335 | void ata_host_resume(struct ata_host *host) | 5334 | void ata_host_resume(struct ata_host *host) |
| 5336 | { | 5335 | { |
| 5337 | ata_host_request_pm(host, PMSG_ON, ATA_EH_RESET, | ||
| 5338 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); | ||
| 5339 | host->dev->power.power_state = PMSG_ON; | 5336 | host->dev->power.power_state = PMSG_ON; |
| 5340 | } | 5337 | } |
| 5341 | #endif | 5338 | #endif |
| 5342 | 5339 | ||
| 5340 | struct device_type ata_port_type = { | ||
| 5341 | .name = "ata_port", | ||
| 5342 | #ifdef CONFIG_PM | ||
| 5343 | .pm = &ata_port_pm_ops, | ||
| 5344 | #endif | ||
| 5345 | }; | ||
| 5346 | |||
| 5343 | /** | 5347 | /** |
| 5344 | * ata_dev_init - Initialize an ata_device structure | 5348 | * ata_dev_init - Initialize an ata_device structure |
| 5345 | * @dev: Device structure to initialize | 5349 | * @dev: Device structure to initialize |
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index ce9dc6207f37..3ceb3d94be26 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c | |||
| @@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent, | |||
| 279 | struct device *dev = &ap->tdev; | 279 | struct device *dev = &ap->tdev; |
| 280 | 280 | ||
| 281 | device_initialize(dev); | 281 | device_initialize(dev); |
| 282 | dev->type = &ata_port_type; | ||
| 282 | 283 | ||
| 283 | dev->parent = get_device(parent); | 284 | dev->parent = get_device(parent); |
| 284 | dev->release = ata_tport_release; | 285 | dev->release = ata_tport_release; |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 773de97988a2..814486d35c44 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
| @@ -58,6 +58,7 @@ extern int atapi_passthru16; | |||
| 58 | extern int libata_fua; | 58 | extern int libata_fua; |
| 59 | extern int libata_noacpi; | 59 | extern int libata_noacpi; |
| 60 | extern int libata_allow_tpm; | 60 | extern int libata_allow_tpm; |
| 61 | extern struct device_type ata_port_type; | ||
| 61 | extern struct ata_link *ata_dev_phys_link(struct ata_device *dev); | 62 | extern struct ata_link *ata_dev_phys_link(struct ata_device *dev); |
| 62 | extern void ata_force_cbl(struct ata_port *ap); | 63 | extern void ata_force_cbl(struct ata_port *ap); |
| 63 | extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); | 64 | extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); |
