[PATCH] Add _GTM and _STM support to libata
Matthew Garrett
mjg59 at srcf.ucam.org
Mon Mar 26 03:29:22 UTC 2007
I've ported the drivers/ide/ide-acpi code to libata. This fixes
suspend/resume on the HP nc6220, and should tidy up a reasonable number
of regressions from edgy. Ben, can you merge this ASAP?
Signed-off-by: Matthew Garrett <mjg59 at srcf.ucam.org>
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index c428a56..0e542ea 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -34,6 +34,19 @@ struct taskfile_array {
u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
};
+struct GTM_buffer {
+ u32 PIO_speed0;
+ u32 DMA_speed0;
+ u32 PIO_speed1;
+ u32 DMA_speed1;
+ u32 GTM_flags;
+};
+
+struct ata_acpi_port_link {
+ acpi_handle obj_handle;
+ struct GTM_buffer gtm;
+};
+
/*
* Helper - belongs in the PCI layer somewhere eventually
*/
@@ -706,4 +719,125 @@ out:
return 0;
}
+/**
+ * ata_acpi_get_timings - get the channel (controller) timings
+ * @hwif: target IDE interface (channel)
+ *
+ * This function executes the _GTM ACPI method for the target channel.
+ *
+ */
+void ata_acpi_get_timings(struct ata_port *ap)
+{
+ acpi_status status;
+ int err;
+ acpi_handle dev_handle = NULL;
+ acpi_integer pcidevfn = 0;
+ struct device *dev = ap->host->dev;
+ struct acpi_buffer output;
+ union acpi_object *out_obj;
+
+ if (noacpi)
+ return;
+
+ if (ap->cbl == ATA_CBL_SATA)
+ return;
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+
+ if (err < 0)
+ return;
+
+ ap->port_handle = acpi_get_child(dev_handle, ap->port_no);
+
+ /* Setting up output buffer for _GTM */
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
+
+ /* _GTM has no input parameters */
+ status = acpi_evaluate_object(ap->port_handle, "_GTM",
+ NULL, &output);
+
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (!output.length || !output.pointer) {
+ kfree(output.pointer);
+ return;
+ }
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ kfree(output.pointer);
+ return;
+ }
+
+ if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+ out_obj->buffer.length != sizeof(struct GTM_buffer)) {
+ kfree(output.pointer);
+ printk(KERN_ERR
+ "%s: unexpected _GTM length (0x%x)[should be 0x%zx] or \
+"
+ "addr (0x%p)\n",
+ __FUNCTION__, out_obj->buffer.length,
+ sizeof(struct GTM_buffer), out_obj->buffer.pointer);
+ return;
+ }
+
+ if (!ap->acpidata) {
+ ap->acpidata = kzalloc(sizeof(struct ata_acpi_port_link), GFP_KERNEL);
+ if (!ap->acpidata)
+ return;
+ }
+
+ memcpy(&ap->acpidata->gtm, out_obj->buffer.pointer,
+ sizeof(struct GTM_buffer));
+
+ kfree(output.pointer);
+}
+EXPORT_SYMBOL_GPL(ata_acpi_get_timings);
+
+/**
+ * ata_acpi_push_timings - set the channel (controller) timings
+ * @hwif: target IDE interface (channel)
+ *
+ * This function executes the _STM ACPI method for the target channel.
+ *
+ * _STM requires Identify Drive data, which has to passed as an argument.
+ * Unfortunately hd_driveid is a mangled version which we can't readily
+ * use; hence we'll get the information afresh.
+ */
+void ata_acpi_push_timings(struct ata_port *ap)
+{
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[3];
+
+ if (noacpi)
+ return;
+
+ if (!ap->acpidata)
+ return;
+
+ /* Give the GTM buffer + drive Identify data to the channel via the
+ * _STM method: */
+ /* setup input parameters buffer for _STM */
+ input.count = 3;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = sizeof(struct GTM_buffer);
+ in_params[0].buffer.pointer = (u8 *)&ap->acpidata->gtm;
+ in_params[1].type = ACPI_TYPE_BUFFER;
+ in_params[1].buffer.length = sizeof(ap->device[0].id[0]) * ATA_ID_WORDS;
+ in_params[1].buffer.pointer = (u8 *)&ap->device[0].id;
+ in_params[2].type = ACPI_TYPE_BUFFER;
+ in_params[2].buffer.length = sizeof(ap->device[1].id[0]) * ATA_ID_WORDS;
+ in_params[2].buffer.pointer = (u8 *)&ap->device[1].id;
+ /* Output buffer: _STM has no output */
+
+ status = acpi_evaluate_object(ap->port_handle, "_STM",
+ &input, NULL);
+
+ if (ACPI_FAILURE(status))
+ return;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_push_timings);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bf327d4..df38fc3 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5444,6 +5444,7 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
goto fail;
}
}
+ ata_acpi_get_timings(ap);
}
host->dev->power.power_state = mesg;
@@ -5467,6 +5468,11 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
*/
void ata_host_resume(struct ata_host *host)
{
+ int i;
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ ata_acpi_push_timings(ap);
+ }
ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET,
ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
host->dev->power.power_state = PMSG_ON;
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index c426714..277982c 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -101,6 +101,8 @@ extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev,
#ifdef CONFIG_SATA_ACPI
extern int ata_acpi_exec_tfs(struct ata_port *ap);
extern int ata_acpi_push_id(struct ata_port *ap, unsigned int ix);
+extern void ata_acpi_get_timings(struct ata_port *ap);
+extern void ata_acpi_push_timings(struct ata_port *ap);
#else
static inline int ata_acpi_exec_tfs(struct ata_port *ap)
{
@@ -110,6 +112,14 @@ static inline int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
{
return 0;
}
+static inline void ata_acpi_get_timings(struct ata_port *ap)
+{
+ return;
+}
+extern void ata_acpi_push_timings(struct ata_port *ap)
+{
+ return;
+}
#endif
/* libata-scsi.c */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index e3f32f3..b141863 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -340,6 +340,9 @@ struct scsi_device;
struct ata_port_operations;
struct ata_port;
struct ata_queued_cmd;
+#ifdef CONFIG_SATA_ACPI
+struct ata_acpi_port_link;
+#endif
/* typedefs */
typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc);
@@ -590,6 +593,11 @@ struct ata_port {
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
+#ifdef CONFIG_SATA_ACPI
+ /* ACPI objects info */
+ acpi_handle port_handle;
+ struct ata_acpi_port_link *acpidata;
+#endif
};
struct ata_port_operations {
--
Matthew Garrett | mjg59 at srcf.ucam.org
More information about the kernel-team
mailing list