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 | |
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>
-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 | ||||
-rw-r--r-- | include/linux/libata.h | 13 |
4 files changed, 171 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 |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 0c4f63b55ec6..8d3e391ab8d3 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -198,6 +198,7 @@ enum { | |||
198 | ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ | 198 | ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ |
199 | ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ | 199 | ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ |
200 | ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ | 200 | ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ |
201 | ATA_PFLAG_GTM_VALID = (1 << 19), /* acpi_gtm data valid */ | ||
201 | 202 | ||
202 | /* struct ata_queued_cmd flags */ | 203 | /* struct ata_queued_cmd flags */ |
203 | ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ | 204 | ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ |
@@ -493,6 +494,17 @@ struct ata_eh_context { | |||
493 | unsigned int did_probe_mask; | 494 | unsigned int did_probe_mask; |
494 | }; | 495 | }; |
495 | 496 | ||
497 | struct ata_acpi_drive | ||
498 | { | ||
499 | u32 pio; | ||
500 | u32 dma; | ||
501 | } __packed; | ||
502 | |||
503 | struct ata_acpi_gtm { | ||
504 | struct ata_acpi_drive drive[2]; | ||
505 | u32 flags; | ||
506 | } __packed; | ||
507 | |||
496 | struct ata_port { | 508 | struct ata_port { |
497 | struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ | 509 | struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ |
498 | const struct ata_port_operations *ops; | 510 | const struct ata_port_operations *ops; |
@@ -555,6 +567,7 @@ struct ata_port { | |||
555 | 567 | ||
556 | #ifdef CONFIG_ATA_ACPI | 568 | #ifdef CONFIG_ATA_ACPI |
557 | acpi_handle acpi_handle; | 569 | acpi_handle acpi_handle; |
570 | struct ata_acpi_gtm acpi_gtm; | ||
558 | #endif | 571 | #endif |
559 | u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */ | 572 | u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */ |
560 | }; | 573 | }; |