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/ata/libata-acpi.c | |
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/ata/libata-acpi.c')
-rw-r--r-- | drivers/ata/libata-acpi.c | 153 |
1 files changed, 149 insertions, 4 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 | ||