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 /drivers/ata | |
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>
Diffstat (limited to 'drivers/ata')
-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); |