Commit 64578a3de723d502621860f9d4d28f34d001b066
Committed by
Jeff Garzik
1 parent
e5fa24dfdb
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>
Showing 4 changed files with 171 additions and 5 deletions Side-by-side Diff
drivers/ata/libata-acpi.c
... | ... | @@ -101,6 +101,108 @@ |
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 | 206 | * ata_dev_get_GTF - get the drive bootup default taskfile settings |
105 | 207 | * @dev: target ATA device |
106 | 208 | * @gtf: output parameter for buffer containing _GTF taskfile arrays |
... | ... | @@ -355,6 +457,46 @@ |
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 | 500 | * ata_acpi_on_resume - ATA ACPI hook called on resume |
359 | 501 | * @ap: target ATA port |
360 | 502 | * |
... | ... | @@ -368,6 +510,13 @@ |
368 | 510 | { |
369 | 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 | 520 | /* schedule _GTF */ |
372 | 521 | for (i = 0; i < ATA_MAX_DEVICES; i++) |
373 | 522 | ap->device[i].flags |= ATA_DFLAG_ACPI_PENDING; |
... | ... | @@ -393,10 +542,6 @@ |
393 | 542 | struct ata_eh_context *ehc = &ap->eh_context; |
394 | 543 | int acpi_sata = ap->flags & ATA_FLAG_ACPI_SATA; |
395 | 544 | int rc; |
396 | - | |
397 | - /* XXX: _STM isn't implemented yet, skip if IDE for now */ | |
398 | - if (!acpi_sata) | |
399 | - return 0; | |
400 | 545 | |
401 | 546 | if (!dev->acpi_handle) |
402 | 547 | return 0; |
drivers/ata/libata-eh.c
... | ... | @@ -2154,19 +2154,25 @@ |
2154 | 2154 | |
2155 | 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 | 2162 | /* suspend */ |
2158 | 2163 | ata_eh_freeze_port(ap); |
2159 | 2164 | |
2160 | 2165 | if (ap->ops->port_suspend) |
2161 | 2166 | rc = ap->ops->port_suspend(ap, ap->pm_mesg); |
2162 | 2167 | |
2168 | + out: | |
2163 | 2169 | /* report result */ |
2164 | 2170 | spin_lock_irqsave(ap->lock, flags); |
2165 | 2171 | |
2166 | 2172 | ap->pflags &= ~ATA_PFLAG_PM_PENDING; |
2167 | 2173 | if (rc == 0) |
2168 | 2174 | ap->pflags |= ATA_PFLAG_SUSPENDED; |
2169 | - else | |
2175 | + else if (ap->pflags & ATA_PFLAG_FROZEN) | |
2170 | 2176 | ata_port_schedule_eh(ap); |
2171 | 2177 | |
2172 | 2178 | if (ap->pm_result) { |
drivers/ata/libata.h
... | ... | @@ -99,10 +99,12 @@ |
99 | 99 | /* libata-acpi.c */ |
100 | 100 | #ifdef CONFIG_ATA_ACPI |
101 | 101 | extern void ata_acpi_associate(struct ata_host *host); |
102 | +extern int ata_acpi_on_suspend(struct ata_port *ap); | |
102 | 103 | extern void ata_acpi_on_resume(struct ata_port *ap); |
103 | 104 | extern int ata_acpi_on_devcfg(struct ata_device *adev); |
104 | 105 | #else |
105 | 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 | 108 | static inline void ata_acpi_on_resume(struct ata_port *ap) { } |
107 | 109 | static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; } |
108 | 110 | #endif |
include/linux/libata.h
... | ... | @@ -198,6 +198,7 @@ |
198 | 198 | ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ |
199 | 199 | ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ |
200 | 200 | ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ |
201 | + ATA_PFLAG_GTM_VALID = (1 << 19), /* acpi_gtm data valid */ | |
201 | 202 | |
202 | 203 | /* struct ata_queued_cmd flags */ |
203 | 204 | ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ |
... | ... | @@ -493,6 +494,17 @@ |
493 | 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 | 508 | struct ata_port { |
497 | 509 | struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ |
498 | 510 | const struct ata_port_operations *ops; |
... | ... | @@ -555,6 +567,7 @@ |
555 | 567 | |
556 | 568 | #ifdef CONFIG_ATA_ACPI |
557 | 569 | acpi_handle acpi_handle; |
570 | + struct ata_acpi_gtm acpi_gtm; | |
558 | 571 | #endif |
559 | 572 | u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */ |
560 | 573 | }; |