diff options
| author | Tejun Heo <htejun@gmail.com> | 2007-05-14 14:28:16 -0400 |
|---|---|---|
| committer | Jeff Garzik <jeff@garzik.org> | 2007-07-09 12:17:32 -0400 |
| commit | 64578a3de723d502621860f9d4d28f34d001b066 (patch) | |
| tree | 821e7ad8e8d6c3a0f3224b479ff000e00001a165 /drivers | |
| parent | e5fa24dfdb522b642dbe9b8b1b692f68dce89835 (diff) | |
libata-acpi: implement _GTM/_STM support
Implement _GTM/_STM support. acpi_gtm is added to ata_port which
stores _GTM parameters over suspend/resume cycle. A new hook
ata_acpi_on_suspend() is responsible for storing _GTM parameters
during suspend. _STM is executed in ata_acpi_on_resume(). With this
change, invoking _GTF is safe on IDE hierarchy and acpi_sata check
before _GTF is removed.
ata_acpi_gtm() and ata_acpi_stm() implementation is taken from Alan
Cox's pata_acpi implementation. ata_acpi_gtm() is fixed such that the
result parameter is not shifted by sizeof(union acpi_object).
Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/ata/libata-acpi.c | 153 | ||||
| -rw-r--r-- | drivers/ata/libata-eh.c | 8 | ||||
| -rw-r--r-- | drivers/ata/libata.h | 2 |
3 files changed, 158 insertions, 5 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 78db2e67d7c7..c059f78ad944 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c | |||
| @@ -101,6 +101,108 @@ void ata_acpi_associate(struct ata_host *host) | |||
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | /** | 103 | /** |
| 104 | * ata_acpi_gtm - execute _GTM | ||
| 105 | * @ap: target ATA port | ||
| 106 | * @gtm: out parameter for _GTM result | ||
| 107 | * | ||
| 108 | * Evaluate _GTM and store the result in @gtm. | ||
| 109 | * | ||
| 110 | * LOCKING: | ||
| 111 | * EH context. | ||
| 112 | * | ||
| 113 | * RETURNS: | ||
| 114 | * 0 on success, -ENOENT if _GTM doesn't exist, -errno on failure. | ||
| 115 | */ | ||
| 116 | static int ata_acpi_gtm(const struct ata_port *ap, struct ata_acpi_gtm *gtm) | ||
| 117 | { | ||
| 118 | struct acpi_buffer output = { .length = ACPI_ALLOCATE_BUFFER }; | ||
| 119 | union acpi_object *out_obj; | ||
| 120 | acpi_status status; | ||
| 121 | int rc = 0; | ||
| 122 | |||
| 123 | status = acpi_evaluate_object(ap->acpi_handle, "_GTM", NULL, &output); | ||
| 124 | |||
| 125 | rc = -ENOENT; | ||
| 126 | if (status == AE_NOT_FOUND) | ||
| 127 | goto out_free; | ||
| 128 | |||
| 129 | rc = -EINVAL; | ||
| 130 | if (ACPI_FAILURE(status)) { | ||
| 131 | ata_port_printk(ap, KERN_ERR, | ||
| 132 | "ACPI get timing mode failed (AE 0x%x)\n", | ||
| 133 | status); | ||
| 134 | goto out_free; | ||
| 135 | } | ||
| 136 | |||
| 137 | out_obj = output.pointer; | ||
| 138 | if (out_obj->type != ACPI_TYPE_BUFFER) { | ||
| 139 | ata_port_printk(ap, KERN_WARNING, | ||
| 140 | "_GTM returned unexpected object type 0x%x\n", | ||
| 141 | out_obj->type); | ||
| 142 | |||
| 143 | goto out_free; | ||
| 144 | } | ||
| 145 | |||
| 146 | if (out_obj->buffer.length != sizeof(struct ata_acpi_gtm)) { | ||
| 147 | ata_port_printk(ap, KERN_ERR, | ||
| 148 | "_GTM returned invalid length %d\n", | ||
| 149 | out_obj->buffer.length); | ||
| 150 | goto out_free; | ||
| 151 | } | ||
| 152 | |||
| 153 | memcpy(gtm, out_obj->buffer.pointer, sizeof(struct ata_acpi_gtm)); | ||
| 154 | rc = 0; | ||
| 155 | out_free: | ||
| 156 | kfree(output.pointer); | ||
| 157 | return rc; | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * ata_acpi_stm - execute _STM | ||
| 162 | * @ap: target ATA port | ||
| 163 | * @stm: timing parameter to _STM | ||
| 164 | * | ||
| 165 | * Evaluate _STM with timing parameter @stm. | ||
| 166 | * | ||
| 167 | * LOCKING: | ||
| 168 | * EH context. | ||
| 169 | * | ||
| 170 | * RETURNS: | ||
| 171 | * 0 on success, -ENOENT if _STM doesn't exist, -errno on failure. | ||
| 172 | */ | ||
| 173 | static int ata_acpi_stm(const struct ata_port *ap, struct ata_acpi_gtm *stm) | ||
| 174 | { | ||
| 175 | acpi_status status; | ||
| 176 | struct acpi_object_list input; | ||
| 177 | union acpi_object in_params[3]; | ||
| 178 | |||
| 179 | in_params[0].type = ACPI_TYPE_BUFFER; | ||
| 180 | in_params[0].buffer.length = sizeof(struct ata_acpi_gtm); | ||
| 181 | in_params[0].buffer.pointer = (u8 *)stm; | ||
| 182 | /* Buffers for id may need byteswapping ? */ | ||
| 183 | in_params[1].type = ACPI_TYPE_BUFFER; | ||
| 184 | in_params[1].buffer.length = 512; | ||
| 185 | in_params[1].buffer.pointer = (u8 *)ap->device[0].id; | ||
| 186 | in_params[2].type = ACPI_TYPE_BUFFER; | ||
| 187 | in_params[2].buffer.length = 512; | ||
| 188 | in_params[2].buffer.pointer = (u8 *)ap->device[1].id; | ||
| 189 | |||
| 190 | input.count = 3; | ||
| 191 | input.pointer = in_params; | ||
| 192 | |||
| 193 | status = acpi_evaluate_object(ap->acpi_handle, "_STM", &input, NULL); | ||
| 194 | |||
| 195 | if (status == AE_NOT_FOUND) | ||
| 196 | return -ENOENT; | ||
| 197 | if (ACPI_FAILURE(status)) { | ||
| 198 | ata_port_printk(ap, KERN_ERR, | ||
| 199 | "ACPI set timing mode failed (status=0x%x)\n", status); | ||
| 200 | return -EINVAL; | ||
| 201 | } | ||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 104 | * ata_dev_get_GTF - get the drive bootup default taskfile settings | 206 | * ata_dev_get_GTF - get the drive bootup default taskfile settings |
| 105 | * @dev: target ATA device | 207 | * @dev: target ATA device |
| 106 | * @gtf: output parameter for buffer containing _GTF taskfile arrays | 208 | * @gtf: output parameter for buffer containing _GTF taskfile arrays |
| @@ -355,6 +457,46 @@ static int ata_acpi_push_id(struct ata_device *dev) | |||
| 355 | } | 457 | } |
| 356 | 458 | ||
| 357 | /** | 459 | /** |
| 460 | * ata_acpi_on_suspend - ATA ACPI hook called on suspend | ||
| 461 | * @ap: target ATA port | ||
| 462 | * | ||
| 463 | * This function is called when @ap is about to be suspended. All | ||
| 464 | * devices are already put to sleep but the port_suspend() callback | ||
| 465 | * hasn't been executed yet. Error return from this function aborts | ||
| 466 | * suspend. | ||
| 467 | * | ||
| 468 | * LOCKING: | ||
| 469 | * EH context. | ||
| 470 | * | ||
| 471 | * RETURNS: | ||
| 472 | * 0 on success, -errno on failure. | ||
| 473 | */ | ||
| 474 | int ata_acpi_on_suspend(struct ata_port *ap) | ||
| 475 | { | ||
| 476 | unsigned long flags; | ||
| 477 | int rc; | ||
| 478 | |||
| 479 | /* proceed iff per-port acpi_handle is valid */ | ||
| 480 | if (!ap->acpi_handle) | ||
| 481 | return 0; | ||
| 482 | BUG_ON(ap->flags & ATA_FLAG_ACPI_SATA); | ||
| 483 | |||
| 484 | /* store timing parameters */ | ||
| 485 | rc = ata_acpi_gtm(ap, &ap->acpi_gtm); | ||
| 486 | |||
| 487 | spin_lock_irqsave(ap->lock, flags); | ||
| 488 | if (rc == 0) | ||
| 489 | ap->pflags |= ATA_PFLAG_GTM_VALID; | ||
| 490 | else | ||
| 491 | ap->pflags &= ~ATA_PFLAG_GTM_VALID; | ||
| 492 | spin_unlock_irqrestore(ap->lock, flags); | ||
| 493 | |||
| 494 | if (rc == -ENOENT) | ||
| 495 | rc = 0; | ||
| 496 | return rc; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 358 | * ata_acpi_on_resume - ATA ACPI hook called on resume | 500 | * ata_acpi_on_resume - ATA ACPI hook called on resume |
| 359 | * @ap: target ATA port | 501 | * @ap: target ATA port |
| 360 | * | 502 | * |
| @@ -368,6 +510,13 @@ void ata_acpi_on_resume(struct ata_port *ap) | |||
| 368 | { | 510 | { |
| 369 | int i; | 511 | int i; |
| 370 | 512 | ||
| 513 | if (ap->acpi_handle && (ap->pflags & ATA_PFLAG_GTM_VALID)) { | ||
| 514 | BUG_ON(ap->flags & ATA_FLAG_ACPI_SATA); | ||
| 515 | |||
| 516 | /* restore timing parameters */ | ||
| 517 | ata_acpi_stm(ap, &ap->acpi_gtm); | ||
| 518 | } | ||
| 519 | |||
| 371 | /* schedule _GTF */ | 520 | /* schedule _GTF */ |
| 372 | for (i = 0; i < ATA_MAX_DEVICES; i++) | 521 | for (i = 0; i < ATA_MAX_DEVICES; i++) |
| 373 | ap->device[i].flags |= ATA_DFLAG_ACPI_PENDING; | 522 | ap->device[i].flags |= ATA_DFLAG_ACPI_PENDING; |
| @@ -394,10 +543,6 @@ int ata_acpi_on_devcfg(struct ata_device *dev) | |||
| 394 | int acpi_sata = ap->flags & ATA_FLAG_ACPI_SATA; | 543 | int acpi_sata = ap->flags & ATA_FLAG_ACPI_SATA; |
| 395 | int rc; | 544 | int rc; |
| 396 | 545 | ||
| 397 | /* XXX: _STM isn't implemented yet, skip if IDE for now */ | ||
| 398 | if (!acpi_sata) | ||
| 399 | return 0; | ||
| 400 | |||
| 401 | if (!dev->acpi_handle) | 546 | if (!dev->acpi_handle) |
| 402 | return 0; | 547 | return 0; |
| 403 | 548 | ||
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index fed217db82d2..9ee0a8c08d96 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
| @@ -2154,19 +2154,25 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) | |||
| 2154 | 2154 | ||
| 2155 | WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); | 2155 | WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); |
| 2156 | 2156 | ||
| 2157 | /* tell ACPI we're suspending */ | ||
| 2158 | rc = ata_acpi_on_suspend(ap); | ||
| 2159 | if (rc) | ||
| 2160 | goto out; | ||
| 2161 | |||
| 2157 | /* suspend */ | 2162 | /* suspend */ |
| 2158 | ata_eh_freeze_port(ap); | 2163 | ata_eh_freeze_port(ap); |
| 2159 | 2164 | ||
| 2160 | if (ap->ops->port_suspend) | 2165 | if (ap->ops->port_suspend) |
| 2161 | rc = ap->ops->port_suspend(ap, ap->pm_mesg); | 2166 | rc = ap->ops->port_suspend(ap, ap->pm_mesg); |
| 2162 | 2167 | ||
| 2168 | out: | ||
| 2163 | /* report result */ | 2169 | /* report result */ |
| 2164 | spin_lock_irqsave(ap->lock, flags); | 2170 | spin_lock_irqsave(ap->lock, flags); |
| 2165 | 2171 | ||
| 2166 | ap->pflags &= ~ATA_PFLAG_PM_PENDING; | 2172 | ap->pflags &= ~ATA_PFLAG_PM_PENDING; |
| 2167 | if (rc == 0) | 2173 | if (rc == 0) |
| 2168 | ap->pflags |= ATA_PFLAG_SUSPENDED; | 2174 | ap->pflags |= ATA_PFLAG_SUSPENDED; |
| 2169 | else | 2175 | else if (ap->pflags & ATA_PFLAG_FROZEN) |
| 2170 | ata_port_schedule_eh(ap); | 2176 | ata_port_schedule_eh(ap); |
| 2171 | 2177 | ||
| 2172 | if (ap->pm_result) { | 2178 | if (ap->pm_result) { |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index bee7cbc4c97c..ba17fc5f2e99 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
| @@ -99,10 +99,12 @@ extern struct ata_port *ata_port_alloc(struct ata_host *host); | |||
| 99 | /* libata-acpi.c */ | 99 | /* libata-acpi.c */ |
| 100 | #ifdef CONFIG_ATA_ACPI | 100 | #ifdef CONFIG_ATA_ACPI |
| 101 | extern void ata_acpi_associate(struct ata_host *host); | 101 | extern void ata_acpi_associate(struct ata_host *host); |
| 102 | extern int ata_acpi_on_suspend(struct ata_port *ap); | ||
| 102 | extern void ata_acpi_on_resume(struct ata_port *ap); | 103 | extern void ata_acpi_on_resume(struct ata_port *ap); |
| 103 | extern int ata_acpi_on_devcfg(struct ata_device *adev); | 104 | extern int ata_acpi_on_devcfg(struct ata_device *adev); |
| 104 | #else | 105 | #else |
| 105 | static inline void ata_acpi_associate(struct ata_host *host) { } | 106 | static inline void ata_acpi_associate(struct ata_host *host) { } |
| 107 | static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; } | ||
| 106 | static inline void ata_acpi_on_resume(struct ata_port *ap) { } | 108 | static inline void ata_acpi_on_resume(struct ata_port *ap) { } |
| 107 | static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; } | 109 | static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; } |
| 108 | #endif | 110 | #endif |
