Skip to contentSkip to conversation

SATA ACPI make/config

PATCH In review Randy Dunlap 2 participants 19 messages Dec 14, 2005 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

PATCH 1/7 SATA suspend/resume support (Jens)

From: Jens Axboe <[email protected]>

Add libata suspend/resume support.

 drivers/scsi/ata_piix.c    |    4 +
 drivers/scsi/libata-core.c |  122 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libata-scsi.c |   16 +++++
 drivers/scsi/scsi_sysfs.c  |   35 ++++++++++++
 include/linux/ata.h        |    2 
 include/linux/libata.h     |    7 ++
 include/scsi/scsi_host.h   |    6 ++
 7 files changed, 190 insertions(+), 2 deletions(-)

---

drivers/scsi/ata_piix.c +4 -0
@@ -125,6 +125,8 @@ static struct pci_driver piix_pci_driver
 	.id_table		= piix_pci_tbl,
 	.probe			= piix_init_one,
 	.remove			= ata_pci_remove_one,
+	.suspend		= ata_pci_device_suspend,
+	.resume			= ata_pci_device_resume,
 };
 
 static struct scsi_host_template piix_sht = {
@@ -145,6 +147,8 @@ static struct scsi_host_template piix_sh
 	.slave_configure	= ata_scsi_slave_config,
 	.bios_param		= ata_std_bios_param,
 	.ordered_flush		= 1,
+	.suspend		= ata_scsi_device_suspend,
+	.resume			= ata_scsi_device_resume,
 };
 
 static const struct ata_port_operations piix_pata_ops = {
drivers/scsi/libata-core.c +122 -0

138 lines not on this page · raw message

drivers/scsi/libata-scsi.c +16 -0
@@ -396,6 +396,22 @@ void ata_dump_status(unsigned id, struct
 	}
 }
 
+int ata_scsi_device_resume(struct scsi_device *sdev)
+{
+	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+	struct ata_device *dev = &ap->device[sdev->id];
+
+	return ata_device_resume(ap, dev);
+}
+
+int ata_scsi_device_suspend(struct scsi_device *sdev)
+{
+	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+	struct ata_device *dev = &ap->device[sdev->id];
+
+	return ata_device_suspend(ap, dev);
+}
+
 /**
  *	ata_to_sense_error - convert ATA error to SCSI error
  *	@id: ATA device number
drivers/scsi/scsi_sysfs.c +33 -2
@@ -263,9 +263,40 @@ static int scsi_bus_match(struct device 
 	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
 }
 
+static int scsi_bus_suspend(struct device * dev, pm_message_t state)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_host_template *sht = sdev->host->hostt;
+	int err;
+
+	err = scsi_device_quiesce(sdev);
+	if (err)
+		return err;
+
+	if (sht->suspend)
+		err = sht->suspend(sdev);
+
+	return err;
+}
+
+static int scsi_bus_resume(struct device * dev)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_host_template *sht = sdev->host->hostt;
+	int err = 0;
+
+	if (sht->resume)
+		err = sht->resume(sdev);
+
+	scsi_device_resume(sdev);
+	return err;
+}
+
 struct bus_type scsi_bus_type = {
-        .name		= "scsi",
-        .match		= scsi_bus_match,
+	.name		= "scsi",
+	.match		= scsi_bus_match,
+	.suspend	= scsi_bus_suspend,
+	.resume		= scsi_bus_resume,
 };
 
 int scsi_sysfs_register(void)
include/linux/ata.h +2 -0
@@ -141,6 +141,8 @@ enum {
 	ATA_CMD_PACKET		= 0xA0,
 	ATA_CMD_VERIFY		= 0x40,
 	ATA_CMD_VERIFY_EXT	= 0x42,
+ 	ATA_CMD_STANDBYNOW1	= 0xE0,
+ 	ATA_CMD_IDLEIMMEDIATE	= 0xE1,
 	ATA_CMD_INIT_DEV_PARAMS	= 0x91,
 
 	/* SETFEATURES stuff */
include/linux/libata.h +7 -0
@@ -122,6 +122,7 @@ enum {
 	ATA_FLAG_NOINTR		= (1 << 9), /* FIXME: Remove this once
 					     * proper HSM is in place. */
 	ATA_FLAG_DEBUGMSG	= (1 << 10),
+	ATA_FLAG_SUSPENDED	= (1 << 11), /* port is suspended */
 
 	ATA_QCFLAG_ACTIVE	= (1 << 1), /* cmd not yet ack'd to scsi lyer */
 	ATA_QCFLAG_SG		= (1 << 3), /* have s/g table? */
@@ -435,6 +436,8 @@ extern void ata_std_ports(struct ata_iop
 extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 			     unsigned int n_ports);
 extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
+extern int ata_pci_device_resume(struct pci_dev *pdev);
 #endif /* CONFIG_PCI */
 extern int ata_device_add(const struct ata_probe_ent *ent);
 extern void ata_host_set_remove(struct ata_host_set *host_set);
@@ -444,6 +447,10 @@ extern int ata_scsi_queuecmd(struct scsi
 extern int ata_scsi_error(struct Scsi_Host *host);
 extern int ata_scsi_release(struct Scsi_Host *host);
 extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+extern int ata_scsi_device_resume(struct scsi_device *);
+extern int ata_scsi_device_suspend(struct scsi_device *);
+extern int ata_device_resume(struct ata_port *, struct ata_device *);
+extern int ata_device_suspend(struct ata_port *, struct ata_device *);
 extern int ata_ratelimit(void);
 
 /*
include/scsi/scsi_host.h +6 -0
@@ -296,6 +296,12 @@ struct scsi_host_template {
 	int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int);
 
 	/*
+	 * suspend support
+	 */
+	int (*suspend)(struct scsi_device *);
+	int (*resume)(struct scsi_device *);
+
+	/*
 	 * Name of proc directory
 	 */
 	char *proc_name;

---

#2 lore raw

Re: [PATCH 1/7] SATA suspend/resume support (Jens)

Quoted 4 lines · Randy Dunlap

From: Jens Axboe <[email protected]>

Add libata suspend/resume support.

Quoted 8 lines
 drivers/scsi/ata_piix.c    |    4 +
 drivers/scsi/libata-core.c |  122 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libata-scsi.c |   16 +++++
 drivers/scsi/scsi_sysfs.c  |   35 ++++++++++++
 include/linux/ata.h        |    2 
 include/linux/libata.h     |    7 ++
 include/scsi/scsi_host.h   |    6 ++
 7 files changed, 190 insertions(+), 2 deletions(-)

What happened to the patch you were attempting to resurrect, which implemented scsi suspend/resume a better way?

Jeff

#3 lore raw

Re: [PATCH 1/7] SATA suspend/resume support (Jens)

Quoted 14 lines · On Wed, 14 Dec 2005 17:07:38 -0500 Jeff Garzik <[email protected]> wrote:
Quoted 4 lines · Randy Dunlap

From: Jens Axboe <[email protected]>

Add libata suspend/resume support.

Quoted 8 lines
 drivers/scsi/ata_piix.c    |    4 +
 drivers/scsi/libata-core.c |  122 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libata-scsi.c |   16 +++++
 drivers/scsi/scsi_sysfs.c  |   35 ++++++++++++
 include/linux/ata.h        |    2 
 include/linux/libata.h     |    7 ++
 include/scsi/scsi_host.h   |    6 ++
 7 files changed, 190 insertions(+), 2 deletions(-)
Quoted 2 lines

What happened to the patch you were attempting to resurrect, which implemented scsi suspend/resume a better way?

The last incarnation of it is still at
http://www.xenotime.net/linux/scsi/scsi-susres-devattr.patch

It is missing some linkage (maybe ->ops) between layers. I was getting close to zero feedback on it --> it went stale, so I decided to try make make Jens's patch work with ACPI additions.

---
~Randy

#4 lore raw

PATCH 2/7 SATA ACPI make/config

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add ata_acpi in Makefile and Kconfig.

---
 drivers/scsi/Kconfig  |    5 +++++
 drivers/scsi/Makefile |    1 +
 2 files changed, 6 insertions(+)
drivers/scsi/Makefile +1 -0
@@ -138,6 +138,7 @@ obj-$(CONFIG_SCSI_SATA_NV)	+= libata.o s
 obj-$(CONFIG_SCSI_SATA_ULI)	+= libata.o sata_uli.o
 obj-$(CONFIG_SCSI_SATA_MV)	+= libata.o sata_mv.o
 obj-$(CONFIG_SCSI_PDC_ADMA)	+= libata.o pdc_adma.o
+obj-$(CONFIG_SCSI_SATA_ACPI)	+= libata.o ata_acpi.o
 
 obj-$(CONFIG_ARM)		+= arm/
 
drivers/scsi/Kconfig +5 -0
@@ -598,6 +598,11 @@ config SCSI_SATA_INTEL_COMBINED
 	depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX)
 	default y
 
+config SCSI_SATA_ACPI
+	bool
+	depends on SCSI_SATA && ACPI
+	default y
+
 config SCSI_BUSLOGIC
 	tristate "BusLogic SCSI support"
 	depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API

---

#6 lore raw

PATCH 3/7 SATA ACPI linux/libata.h update

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add ACPI data pointer/length holders.

---
 include/linux/libata.h |    8 ++++++++
 1 files changed, 8 insertions(+)
include/linux/libata.h +8 -0
@@ -33,6 +33,7 @@
 #include <asm/io.h>
 #include <linux/ata.h>
 #include <linux/workqueue.h>
+#include <acpi/acpi.h>
 
 /*
  * compile-time options
@@ -311,6 +312,13 @@ struct ata_device {
 	u16			cylinders;	/* Number of cylinders */
 	u16			heads;		/* Number of heads */
 	u16			sectors;	/* Number of sectors per track */
+
+#ifdef CONFIG_SCSI_SATA_ACPI
+	/* ACPI objects info */
+	acpi_handle		obj_handle;
+	acpi_size		gtf_length;
+	u8			*gtf_address;
+#endif
 };
 
 struct ata_port {

---

#8 lore raw

Re: [PATCH 3/7] SATA ACPI linux/libata.h update

Quoted 8 lines · On Wed, 14 Dec 2005 17:08:33 -0500 Jeff Garzik <[email protected]> wrote:
Quoted 5 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add ACPI data pointer/length holders.

Why does struct ata_device need to remember this info at all?

Uh... I dunno. I'll see if I can just read that data during resume.

---
~Randy

#9 lore raw

PATCH 4/7 call/invoke SATA ACPI suspend/resume functions

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add calls to ACPI methods for SATA drives.

---
 drivers/scsi/libata-core.c |    4 ++++
 drivers/scsi/libata.h      |   26 ++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)
drivers/scsi/libata-core.c +4 -0
@@ -1345,6 +1345,8 @@ void ata_dev_config(struct ata_port *ap,
 
 	if (ap->ops->dev_config)
 		ap->ops->dev_config(ap, &ap->device[i]);
+
+	do_drive_SDD(ap, i);
 }
 
 /**
@@ -1784,6 +1786,8 @@ static void ata_set_mode(struct ata_port
 	if (ap->flags & ATA_FLAG_PORT_DISABLED)
 		return;
 
+	do_drive_update_taskfiles(ap);
+
 	if (ap->ops->post_set_mode)
 		ap->ops->post_set_mode(ap);
 
drivers/scsi/libata.h +26 -0
@@ -53,6 +53,32 @@ extern int ata_task_ioctl(struct scsi_de
 extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
 
 
+/* ata_acpi.c */
+#ifdef CONFIG_SCSI_SATA_ACPI
+extern int do_drive_SDD(struct ata_port *ap, unsigned int ix);
+extern int do_drive_get_GTF(struct ata_port *ap, struct ata_device *atadev);
+extern int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev);
+extern int do_drive_update_taskfiles(struct ata_port *ap);
+#else
+static inline int do_drive_SDD(struct ata_port *ap, unsigned int ix)
+{
+	return 0;
+}
+static inline int do_drive_get_GTF(struct ata_port *ap, struct ata_device *atadev)
+{
+	return 0;
+}
+static inline int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev)
+{
+	return 0;
+}
+static inline int do_drive_update_taskfiles(struct ata_port *ap)
+{
+	return 0;
+}
+#endif
+
+
 /* libata-scsi.c */
 extern void ata_scsi_scan_host(struct ata_port *ap);
 extern int ata_scsi_error(struct Scsi_Host *host);

---

#10 lore raw

Re: [PATCH 4/7] call/invoke SATA ACPI suspend/resume functions

Quoted 22 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add calls to ACPI methods for SATA drives.

---
 drivers/scsi/libata-core.c |    4 ++++
 drivers/scsi/libata.h      |   26 ++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)
drivers/scsi/libata-core.c +2 -0
@@ -1345,6 +1345,8 @@ void ata_dev_config(struct ata_port *ap,
 
 	if (ap->ops->dev_config)
 		ap->ops->dev_config(ap, &ap->device[i]);
+
+	do_drive_SDD(ap, i);
 }
 
 /**

drivers/scsi/libata-core.c static void ata_set_mode(struct ata_port · line 1793

Quoted 8 lines
drivers/scsi/libata-core.c +2 -0
@@ -1784,6 +1786,8 @@ static void ata_set_mode(struct ata_port
 	if (ap->flags & ATA_FLAG_PORT_DISABLED)
 		return;
 
+	do_drive_update_taskfiles(ap);
+
 	if (ap->ops->post_set_mode)
 		ap->ops->post_set_mode(ap);
 

Mostly OK.

1) The function names are awful. I would suggest ata_acpi_push_id() and ata_acpi_exec_tfs() instead.

2) The call to do_drive_update_taskfiles() should be done in ata_bus_probe(), not inside ata_set_mode() where it is essentially hidden amongst code that is dedicated to a single purpose, getting the data xfer timings.

Jeff

#11 lore raw

PATCH 5/7 SATA ACPI kernel-doc

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add ata_acpi.c to libata kernel-doc template file.

---
 Documentation/DocBook/libata.tmpl |    6 ++++++
 1 files changed, 6 insertions(+)
Documentation/DocBook/libata.tmpl +6 -0
@@ -787,6 +787,12 @@ and other resources, etc.
 !Idrivers/scsi/libata-scsi.c
   </chapter>
 
+  <chapter id="libataAcpi">
+     <title>libata ACPI interfaces/methods</title>
+!Edrivers/scsi/ata_acpi.c
+!Idrivers/scsi/ata_acpi.c
+  </chapter>
+
   <chapter id="ataExceptions">
      <title>ATA errors &amp; exceptions</title>
 

---

#13 lore raw

PATCH 6/7 SATA ACPI suspend/resume functions

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add support for ACPI methods to SATA suspend/resume.

---
 drivers/scsi/ata_acpi.c |  496 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 496 insertions(+)
drivers/scsi/ata_acpi.c new file +496 -0
@@ -0,0 +1,496 @@
+/*
+ * ata-acpi.c
+ * Provides ACPI support for PATA/SATA.
+ *
+ * Copyright (C) 2005 Intel Corp.
+ * Copyright (C) 2005 Randy Dunlap
+ */
+
+#include <linux/ata.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <acpi/acpi.h>
+#include "scsi.h"
+#include <linux/libata.h>
+#include <linux/pci.h>
+#include "libata.h"
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acnames.h>
+#include <acpi/acnamesp.h>
+#include <acpi/acparser.h>
+#include <acpi/acexcep.h>
+#include <acpi/acmacros.h>
+#include <acpi/actypes.h>
+
+#define SATA_ROOT_PORT(x)	(((x) >> 16) & 0xffff)
+#define SATA_PORT_NUMBER(x)	((x) & 0xffff)	/* or NO_PORT_MULT */
+#define NO_PORT_MULT		0xffff
+#define SATA_ADR_RSVD		0xffffffff
+
+#define REGS_PER_GTF		7
+struct taskfile_array {
+	u8	tfa[REGS_PER_GTF];	/* regs. 0x1f1 - 0x1f7 */
+};
+
+#define DEBUGGING	1
+/* note: adds function name and KERN_DEBUG */
+#ifdef DEBUGGING
+#define DEBPRINT(fmt, args...)	\
+		printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DEBPRINT(fmt, args...)	do {} while (0)
+#endif	/* DEBUGGING */
+
+static u8 *acpi_path_name(acpi_handle handle)
+{
+	acpi_status		status;
+	static u8		path_name[ACPI_PATHNAME_MAX];
+	struct acpi_buffer	ret_buf = { ACPI_PATHNAME_MAX, path_name };
+
+	memset(path_name, 0, sizeof (path_name));
+	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
+
+	if (ACPI_FAILURE(status))
+		return NULL;
+
+	return path_name;
+}
+
+/**
+ * pci_acpi_get_dev_handle - finds acpi_handle and pci device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int pci_acpi_get_dev_handle(struct device *dev, acpi_handle *handle,
+					acpi_integer *pcidevfn)
+{
+	struct pci_dev	*pci_dev;
+	acpi_integer	addr;
+
+	pci_dev = to_pci_dev(dev);
+	/* Please refer to the ACPI spec for the syntax of _ADR. */
+	addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+	*pcidevfn = addr;
+	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+	DEBPRINT("pcidevfn: 0x%llx\n",
+		(unsigned long long)*pcidevfn);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}
+
+struct walk_info {		/* can be trimmed some */
+	struct device	*dev;
+	struct acpi_device *adev;
+	acpi_handle	handle;
+	acpi_integer	pcidevfn;
+	unsigned int	drivenum;
+	acpi_handle	obj_handle;
+	struct ata_port *ataport;
+	struct ata_device *atadev;
+	u32		sata_adr;
+	int		status;
+	char		basepath[ACPI_PATHNAME_MAX];
+	int		basepath_len;
+};
+
+static acpi_status get_devices(acpi_handle handle,
+				u32 level, void *context, void **return_value)
+{
+	acpi_status		status;
+	struct walk_info	*winfo = context;
+	u8			*path_name = acpi_path_name(handle);
+	struct acpi_buffer	buffer;
+	struct acpi_device_info	*dinfo;
+
+	buffer.length = ACPI_ALLOCATE_BUFFER;
+	buffer.pointer = NULL;
+	status = acpi_get_object_info(handle, &buffer);
+
+	if (ACPI_SUCCESS(status)) {
+		dinfo = buffer.pointer;
+
+		/* find full device path name for pcidevfn */
+		if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+		    dinfo->address == winfo->pcidevfn) {
+			DEBPRINT(":%s: matches pcidevfn (0x%llx)\n",
+				path_name, winfo->pcidevfn);
+			strlcpy(winfo->basepath, path_name,
+				sizeof(winfo->basepath));
+			winfo->basepath_len = strlen(path_name);
+			goto out;
+		}
+
+		/* if basepath is not yet known, ignore this object */
+		if (!winfo->basepath_len)
+			goto out;
+
+		/* if this object is in scope of basepath, maybe use it */
+		if (strncmp(path_name, winfo->basepath,
+		    winfo->basepath_len) == 0) {
+			if (!(dinfo->valid & ACPI_VALID_ADR))
+				goto out;
+			DEBPRINT("GOT ONE: "
+				"(%s) root_port = 0x%llx, port_num = 0x%llx\n",
+				path_name,
+				SATA_ROOT_PORT(dinfo->address),
+				SATA_PORT_NUMBER(dinfo->address));
+			/* heuristics: */
+			if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT)
+				DEBPRINT("warning: don't know how to handle SATA port multiplier\n");
+			if (SATA_ROOT_PORT(dinfo->address) ==
+				winfo->ataport->port_no &&
+			    SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) {
+				DEBPRINT("THIS ^^^^^ is the requested SATA drive (handle = 0x%p)\n",
+					handle);
+				winfo->sata_adr = dinfo->address;
+				winfo->obj_handle = handle;
+			}
+		}
+out:
+		acpi_os_free(dinfo);
+	}
+
+	return status;
+}
+
+/* Get the SATA drive _ADR object. */
+static int get_sata_adr(struct device *dev, acpi_handle handle,
+			acpi_integer pcidevfn, unsigned int drive,
+			struct ata_port *ap,
+			struct ata_device *atadev, u32 *dev_adr)
+{
+	acpi_status	status;
+	struct walk_info *winfo;
+	int		err = -ENOMEM;
+
+	winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL);
+	if (!winfo)
+		goto out;
+
+	winfo->dev = dev;
+	winfo->atadev = atadev;
+	winfo->ataport = ap;
+	if (acpi_bus_get_device(handle, &winfo->adev) < 0)
+		DEBPRINT("acpi_bus_get_device failed\n");
+	winfo->handle = handle;
+	winfo->pcidevfn = pcidevfn;
+	winfo->drivenum = drive;
+
+	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
+	if (ACPI_FAILURE(status))
+		err = -ENODEV;
+	else {
+		*dev_adr = winfo->sata_adr;
+		atadev->obj_handle = winfo->obj_handle;
+		err = 0;
+	}
+	kfree(winfo);
+out:
+	return err;
+}
+
+/**
+ * do_drive_SDD - send Identify data to a drive
+ * @ap: the ata_port for the drive
+ * @ix: drive index
+ *
+ * Must be after Identify (Packet) Device -- uses its data.
+ */
+int do_drive_SDD(struct ata_port *ap, unsigned int ix)
+{
+	acpi_handle			handle;
+	acpi_integer			pcidevfn;
+	int				err = -ENODEV;
+	struct device			*dev = ap->host_set->dev;
+	struct ata_device		*atadev = &ap->device[ix];
+	u32				dev_adr;
+	acpi_status			status;
+	struct acpi_object_list		input;
+	union acpi_object 		in_params[1];
+
+	printk(KERN_DEBUG
+		"%s: ap->id: %d, ix = %d, port#: %d, hard_port#: %d\n",
+		__FUNCTION__, ap->id, ix, ap->port_no, ap->hard_port_no);
+
+	/* Don't continue if not a SATA device. */
+	if (!ata_id_is_sata(atadev->id))
+		goto out;
+
+	/* Don't continue if device has no _ADR method.
+	 * _SDD is intended for known motherboard devices. */
+	err = pci_acpi_get_dev_handle(dev, &handle, &pcidevfn);
+	if (err < 0)
+		goto out;
+	/* Get this drive's _ADR info. */
+	dev_adr = SATA_ADR_RSVD;
+	err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev, &dev_adr);
+	if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle)
+		goto out;
+
+	/* Give the drive Identify data to the drive via the _SDD method */
+	/* _SDD: set up input parameters */
+	input.count = 1;
+	input.pointer = in_params;
+	in_params[0].type = ACPI_TYPE_BUFFER;
+	in_params[0].buffer.length = sizeof(atadev->id);
+	in_params[0].buffer.pointer = (u8 *)atadev->id;
+	/* Output buffer: _SDD has no output */
+
+	/* It's OK for _SDD to be missing too. */
+	status = acpi_evaluate_object(atadev->obj_handle, "_SDD", &input, NULL);
+	err = ACPI_FAILURE(status) ? -EIO : 0;
+	if (err < 0) {
+		printk(KERN_DEBUG
+			"ata%u(%u): %s _SDD error: status = 0x%x\n",
+			ap->id, ap->device->devno, __FUNCTION__, status);
+	}
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_SDD);
+
+/**
+ * do_drive_get_GTF - get the drive bootup default taskfile settings
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * The _GTF method has no input parameters.
+ * It returns a variable number of register set values (registers
+ * hex 1F1..1F7, taskfiles).
+ * The <variable number> is not known in advance, so have ACPI-CA
+ * allocate the buffer as needed and return it, then free it later.
+ *
+ */
+int do_drive_get_GTF(struct ata_port *ap, struct ata_device *atadev)
+{
+	acpi_status			status;
+	acpi_handle			handle;
+	acpi_integer			pcidevfn;
+	u32				dev_adr;
+	struct acpi_buffer		output;
+	union acpi_object 		*out_obj;
+	struct device			*dev = ap->host_set->dev;
+	int				err = -ENODEV;
+
+	if (!ata_dev_present(atadev))	/* or port disabled ? */
+		goto out;
+
+	/* Don't continue if device has no _ADR method.
+	 * _GTF is intended for known motherboard devices. */
+	err = pci_acpi_get_dev_handle(dev, &handle, &pcidevfn);
+	if (err < 0)
+		goto out;
+
+	/* Get this drive's _ADR info. if _SDD didn't get it. */
+	if (!atadev->obj_handle) {
+		dev_adr = SATA_ADR_RSVD;
+		err = get_sata_adr(dev, handle, pcidevfn, 0, ap, atadev, &dev_adr);
+		if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle)
+			goto out;
+	}
+
+	/* Setting up output buffer */
+	output.length = ACPI_ALLOCATE_BUFFER;
+	output.pointer = NULL;	/* ACPI-CA sets this; save/free it later */
+
+	/* _GTF has no input parameters */
+	err = -EIO;
+	status = acpi_evaluate_object(atadev->obj_handle, "_GTF",
+					NULL, &output);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_DEBUG
+			"%s: Run _GTF error: status = 0x%x\n",
+			__FUNCTION__, status);
+		goto out;
+	}
+
+	if (!output.length || !output.pointer) {
+		printk(KERN_DEBUG
+			"%s: Run _GTF: length or ptr is NULL (0x%llx, 0x%p)\n",
+			__FUNCTION__,
+			(unsigned long long)output.length, output.pointer);
+		acpi_os_free(output.pointer);
+		goto out;
+	}
+
+	out_obj = output.pointer;
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		acpi_os_free(output.pointer);
+		printk(KERN_DEBUG "%s: Run _GTF: error: "
+			"expected object type of ACPI_TYPE_BUFFER, got 0x%x\n",
+			__FUNCTION__, out_obj->type);
+		err = -ENOENT;
+		goto out;
+	}
+
+	atadev->gtf_length = out_obj->buffer.length;
+	atadev->gtf_address = out_obj->buffer.pointer;
+	err = 0;
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_get_GTF);
+
+/**
+ *	taskfile_load_raw - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
+ *
+ *	Outputs ATA taskfile to standard ATA host controller using MMIO
+ *	or PIO as indicated by the ATA_FLAG_MMIO flag.
+ *	Writes the control, feature, nsect, lbal, lbam, and lbah registers.
+ *	Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect,
+ *	hob_lbal, hob_lbam, and hob_lbah.
+ *
+ *	This function waits for idle (!BUSY and !DRQ) after writing
+ *	registers.  If the control register has a new value, this
+ *	function also waits for idle after writing control and before
+ *	writing the remaining registers.
+ *
+ *	LOCKING: TBD:
+ *	Inherited from caller.
+ */
+static void taskfile_load_raw(struct ata_port *ap,
+				struct ata_device *atadev,
+				const struct taskfile_array *gtf)
+{
+	DEBPRINT("(0x1f1-1f7): hex: %02x %02x %02x %02x %02x %02x %02x\n",
+		gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
+		gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
+
+	if (ap->ops->tf_load && ap->ops->exec_command) {
+		struct ata_taskfile atf;
+
+		/* convert gtf to atf */
+		ata_tf_init(ap, &atf, 0);
+		atf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+		atf.protocol = ATA_PROT_NODATA; /* or ATA_PROT_ATAPI_NODATA */
+		atf.feature = gtf->tfa[0];	/* 0x1f1 */
+		atf.nsect   = gtf->tfa[1];	/* 0x1f2 */
+		atf.lbal    = gtf->tfa[2];	/* 0x1f3 */
+		atf.lbam    = gtf->tfa[3];	/* 0x1f4 */
+		atf.lbah    = gtf->tfa[4];	/* 0x1f5 */
+		atf.device  = gtf->tfa[5];	/* 0x1f6 */
+		atf.command = gtf->tfa[6];	/* 0x1f7 */
+
+		DEBPRINT("call tf_load:\n");
+		ap->ops->tf_load(ap, &atf);
+		DEBPRINT("call exec_command:\n");
+		ap->ops->exec_command(ap, &atf);
+		DEBPRINT("tf_load & exec_command done.\n");
+	} else if (ap->ops->qc_issue && ap->ops->qc_prep) {
+		int ret;
+		struct ata_queued_cmd *qc;
+		unsigned long flags;
+		DECLARE_COMPLETION(wait);
+
+		qc = ata_qc_new_init(ap, atadev);
+		if (!qc) {
+			printk(KERN_DEBUG "%s: ata_qc_new_init ret. NULL\n",
+				__FUNCTION__);
+			return;
+		}
+
+		/* convert gtf to qc.tf */
+		qc->tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+		qc->tf.protocol = ATA_PROT_NODATA; /* or ATA_PROT_ATAPI_NODATA */
+		qc->tf.feature = gtf->tfa[0];	/* 0x1f1 */
+		qc->tf.nsect   = gtf->tfa[1];	/* 0x1f2 */
+		qc->tf.lbal    = gtf->tfa[2];	/* 0x1f3 */
+		qc->tf.lbam    = gtf->tfa[3];	/* 0x1f4 */
+		qc->tf.lbah    = gtf->tfa[4];	/* 0x1f5 */
+		qc->tf.device  = gtf->tfa[5];	/* 0x1f6 */
+		qc->tf.command = gtf->tfa[6];	/* 0x1f7 */
+
+		qc->waiting = &wait;
+		qc->complete_fn = ata_qc_complete_noop;
+
+		// TBD: fix locking and return value/wait_for_completion here:
+		DEBPRINT("call ata_qc_issue:\n");
+		spin_lock_irqsave(&ap->host_set->lock, flags);
+		ret = ata_qc_issue(qc);
+		spin_unlock_irqrestore(&ap->host_set->lock, flags);
+		DEBPRINT("ata_qc_issue done: ret = 0x%x\n", ret);
+
+		if (!ret)
+			wait_for_completion(&wait);
+	} else
+		printk(KERN_WARNING "%s: SATA driver is missing tf_load/exec_command or qc_issue function entry points\n",
+			__FUNCTION__);
+}
+
+/**
+ * do_drive_set_taskfiles - write the drive taskfile settings from _GTF
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * Write {atadev->gtf_address, length atadev->gtf_length} in groups of
+ * REGS_PER_GTF bytes.
+ *
+ */
+int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev)
+{
+	int				err = -ENODEV;
+	int				gtf_count =
+					atadev->gtf_length / REGS_PER_GTF;
+	int				ix;
+	struct taskfile_array		*gtf;
+
+	if (!ata_dev_present(atadev))	/* or port disabled ? */
+		goto out;
+	if (!gtf_count)		/* shouldn't be here */
+		goto out;
+
+	DEBPRINT("total GTF bytes = %lld (0x%llx), gtf_count = %d\n",
+		(unsigned long long)atadev->gtf_length,
+		(unsigned long long)atadev->gtf_length, gtf_count);
+	if (atadev->gtf_length % REGS_PER_GTF) {
+		printk(KERN_ERR "%s: unexpected GTF length (%zd)\n",
+			__FUNCTION__, atadev->gtf_length);
+		goto out;
+	}
+
+	for (ix = 0; ix < gtf_count; ix++) {
+		gtf = (struct taskfile_array *)
+			(atadev->gtf_address + ix * REGS_PER_GTF);
+
+		/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
+		taskfile_load_raw(ap, atadev, gtf);
+	}
+
+	err = 0;
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_set_taskfiles);
+
+/**
+ * do_drive_update_taskfiles - get then write drive taskfile settings
+ * @ap: the ata_port for the drive
+ *
+ * This applies to both PATA and SATA drives.
+ */
+int do_drive_update_taskfiles(struct ata_port *ap)
+{
+	int ix;
+	int ret;
+
+	for (ix = 0; ix < ATA_MAX_DEVICES; ix++) {
+		ret = do_drive_get_GTF(ap, &ap->device[ix]);
+		if (ret >= 0)
+			ret = do_drive_set_taskfiles(ap, &ap->device[ix]);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(do_drive_update_taskfiles);

---

#14 lore raw

Re: [PATCH 6/7] SATA ACPI suspend/resume functions

Quoted 61 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add support for ACPI methods to SATA suspend/resume.

---
 drivers/scsi/ata_acpi.c |  496 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 496 insertions(+)
drivers/scsi/ata_acpi.c new file +49 -0
@@ -0,0 +1,496 @@
+/*
+ * ata-acpi.c
+ * Provides ACPI support for PATA/SATA.
+ *
+ * Copyright (C) 2005 Intel Corp.
+ * Copyright (C) 2005 Randy Dunlap
+ */
+
+#include <linux/ata.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <acpi/acpi.h>
+#include "scsi.h"
+#include <linux/libata.h>
+#include <linux/pci.h>
+#include "libata.h"
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acnames.h>
+#include <acpi/acnamesp.h>
+#include <acpi/acparser.h>
+#include <acpi/acexcep.h>
+#include <acpi/acmacros.h>
+#include <acpi/actypes.h>
+
+#define SATA_ROOT_PORT(x)	(((x) >> 16) & 0xffff)
+#define SATA_PORT_NUMBER(x)	((x) & 0xffff)	/* or NO_PORT_MULT */
+#define NO_PORT_MULT		0xffff
+#define SATA_ADR_RSVD		0xffffffff
+
+#define REGS_PER_GTF		7
+struct taskfile_array {
+	u8	tfa[REGS_PER_GTF];	/* regs. 0x1f1 - 0x1f7 */
+};
+
+#define DEBUGGING	1
+/* note: adds function name and KERN_DEBUG */
+#ifdef DEBUGGING
+#define DEBPRINT(fmt, args...)	\
+		printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DEBPRINT(fmt, args...)	do {} while (0)
+#endif	/* DEBUGGING */
+
+static u8 *acpi_path_name(acpi_handle handle)
+{
+	acpi_status		status;
+	static u8		path_name[ACPI_PATHNAME_MAX];

drivers/scsi/ata_acpi.c line 59

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+	struct acpi_buffer	ret_buf = { ACPI_PATHNAME_MAX, path_name };
+
+	memset(path_name, 0, sizeof (path_name));
+	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
+
+	if (ACPI_FAILURE(status))
+		return NULL;
+
+	return path_name;
+}

this should be in generic code somewhere

Quoted 15 lines
drivers/scsi/ata_acpi.c new file +15 -0
+/**
+ * pci_acpi_get_dev_handle - finds acpi_handle and pci device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int pci_acpi_get_dev_handle(struct device *dev, acpi_handle *handle,
+					acpi_integer *pcidevfn)
+{
+	struct pci_dev	*pci_dev;
+	acpi_integer	addr;
+
+	pci_dev = to_pci_dev(dev);

drivers/scsi/ata_acpi.c line 85

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+	/* Please refer to the ACPI spec for the syntax of _ADR. */
+	addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+	*pcidevfn = addr;
+	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+	DEBPRINT("pcidevfn: 0x%llx\n",
+		(unsigned long long)*pcidevfn);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}

this should be in generic code somewhere

Quoted 147 lines
drivers/scsi/ata_acpi.c new file +145 -0
+struct walk_info {		/* can be trimmed some */
+	struct device	*dev;
+	struct acpi_device *adev;
+	acpi_handle	handle;
+	acpi_integer	pcidevfn;
+	unsigned int	drivenum;
+	acpi_handle	obj_handle;
+	struct ata_port *ataport;
+	struct ata_device *atadev;
+	u32		sata_adr;
+	int		status;
+	char		basepath[ACPI_PATHNAME_MAX];
+	int		basepath_len;
+};
+
+static acpi_status get_devices(acpi_handle handle,
+				u32 level, void *context, void **return_value)
+{
+	acpi_status		status;
+	struct walk_info	*winfo = context;
+	u8			*path_name = acpi_path_name(handle);
+	struct acpi_buffer	buffer;
+	struct acpi_device_info	*dinfo;
+
+	buffer.length = ACPI_ALLOCATE_BUFFER;
+	buffer.pointer = NULL;
+	status = acpi_get_object_info(handle, &buffer);
+
+	if (ACPI_SUCCESS(status)) {
+		dinfo = buffer.pointer;
+
+		/* find full device path name for pcidevfn */
+		if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+		    dinfo->address == winfo->pcidevfn) {
+			DEBPRINT(":%s: matches pcidevfn (0x%llx)\n",
+				path_name, winfo->pcidevfn);
+			strlcpy(winfo->basepath, path_name,
+				sizeof(winfo->basepath));
+			winfo->basepath_len = strlen(path_name);
+			goto out;
+		}
+
+		/* if basepath is not yet known, ignore this object */
+		if (!winfo->basepath_len)
+			goto out;
+
+		/* if this object is in scope of basepath, maybe use it */
+		if (strncmp(path_name, winfo->basepath,
+		    winfo->basepath_len) == 0) {
+			if (!(dinfo->valid & ACPI_VALID_ADR))
+				goto out;
+			DEBPRINT("GOT ONE: "
+				"(%s) root_port = 0x%llx, port_num = 0x%llx\n",
+				path_name,
+				SATA_ROOT_PORT(dinfo->address),
+				SATA_PORT_NUMBER(dinfo->address));
+			/* heuristics: */
+			if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT)
+				DEBPRINT("warning: don't know how to handle SATA port multiplier\n");
+			if (SATA_ROOT_PORT(dinfo->address) ==
+				winfo->ataport->port_no &&
+			    SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) {
+				DEBPRINT("THIS ^^^^^ is the requested SATA drive (handle = 0x%p)\n",
+					handle);
+				winfo->sata_adr = dinfo->address;
+				winfo->obj_handle = handle;
+			}
+		}
+out:
+		acpi_os_free(dinfo);
+	}
+
+	return status;
+}
 
 
+/* Get the SATA drive _ADR object. */
+static int get_sata_adr(struct device *dev, acpi_handle handle,
+			acpi_integer pcidevfn, unsigned int drive,
+			struct ata_port *ap,
+			struct ata_device *atadev, u32 *dev_adr)
+{
+	acpi_status	status;
+	struct walk_info *winfo;
+	int		err = -ENOMEM;
+
+	winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL);
+	if (!winfo)
+		goto out;
+
+	winfo->dev = dev;
+	winfo->atadev = atadev;
+	winfo->ataport = ap;
+	if (acpi_bus_get_device(handle, &winfo->adev) < 0)
+		DEBPRINT("acpi_bus_get_device failed\n");
+	winfo->handle = handle;
+	winfo->pcidevfn = pcidevfn;
+	winfo->drivenum = drive;
+
+	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
+	if (ACPI_FAILURE(status))
+		err = -ENODEV;
+	else {
+		*dev_adr = winfo->sata_adr;
+		atadev->obj_handle = winfo->obj_handle;
+		err = 0;
+	}
+	kfree(winfo);
+out:
+	return err;
+}
+
+/**
+ * do_drive_SDD - send Identify data to a drive
+ * @ap: the ata_port for the drive
+ * @ix: drive index
+ *
+ * Must be after Identify (Packet) Device -- uses its data.
+ */
+int do_drive_SDD(struct ata_port *ap, unsigned int ix)
+{
+	acpi_handle			handle;
+	acpi_integer			pcidevfn;
+	int				err = -ENODEV;
+	struct device			*dev = ap->host_set->dev;
+	struct ata_device		*atadev = &ap->device[ix];
+	u32				dev_adr;
+	acpi_status			status;
+	struct acpi_object_list		input;
+	union acpi_object 		in_params[1];
+
+	printk(KERN_DEBUG
+		"%s: ap->id: %d, ix = %d, port#: %d, hard_port#: %d\n",
+		__FUNCTION__, ap->id, ix, ap->port_no, ap->hard_port_no);
+
+	/* Don't continue if not a SATA device. */
+	if (!ata_id_is_sata(atadev->id))
+		goto out;
+
+	/* Don't continue if device has no _ADR method.
+	 * _SDD is intended for known motherboard devices. */
+	err = pci_acpi_get_dev_handle(dev, &handle, &pcidevfn);
+	if (err < 0)
+		goto out;
+	/* Get this drive's _ADR info. */
+	dev_adr = SATA_ADR_RSVD;
+	err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev, &dev_adr);

drivers/scsi/ata_acpi.c line 242

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+	if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle)
+		goto out;
+
+	/* Give the drive Identify data to the drive via the _SDD method */
+	/* _SDD: set up input parameters */
+	input.count = 1;
+	input.pointer = in_params;
+	in_params[0].type = ACPI_TYPE_BUFFER;
+	in_params[0].buffer.length = sizeof(atadev->id);
+	in_params[0].buffer.pointer = (u8 *)atadev->id;

libata runs swap_buf_le16() on atadev->id. Are you sure you don't need swap the buffer before sending?

Quoted 31 lines
drivers/scsi/ata_acpi.c new file +31 -0
+	/* Output buffer: _SDD has no output */
+
+	/* It's OK for _SDD to be missing too. */
+	status = acpi_evaluate_object(atadev->obj_handle, "_SDD", &input, NULL);
+	err = ACPI_FAILURE(status) ? -EIO : 0;
+	if (err < 0) {
+		printk(KERN_DEBUG
+			"ata%u(%u): %s _SDD error: status = 0x%x\n",
+			ap->id, ap->device->devno, __FUNCTION__, status);
+	}
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_SDD);
+
+/**
+ * do_drive_get_GTF - get the drive bootup default taskfile settings
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * The _GTF method has no input parameters.
+ * It returns a variable number of register set values (registers
+ * hex 1F1..1F7, taskfiles).
+ * The <variable number> is not known in advance, so have ACPI-CA
+ * allocate the buffer as needed and return it, then free it later.
+ *
+ */
+int do_drive_get_GTF(struct ata_port *ap, struct ata_device *atadev)
+{

drivers/scsi/ata_acpi.c line 283

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+	acpi_status			status;
+	acpi_handle			handle;
+	acpi_integer			pcidevfn;
+	u32				dev_adr;
+	struct acpi_buffer		output;
+	union acpi_object 		*out_obj;
+	struct device			*dev = ap->host_set->dev;
+	int				err = -ENODEV;
+
+	if (!ata_dev_present(atadev))	/* or port disabled ? */

yes, you should check for port disabled too

Quoted 95 lines
drivers/scsi/ata_acpi.c new file +95 -0
+		goto out;
+
+	/* Don't continue if device has no _ADR method.
+	 * _GTF is intended for known motherboard devices. */
+	err = pci_acpi_get_dev_handle(dev, &handle, &pcidevfn);
+	if (err < 0)
+		goto out;
+
+	/* Get this drive's _ADR info. if _SDD didn't get it. */
+	if (!atadev->obj_handle) {
+		dev_adr = SATA_ADR_RSVD;
+		err = get_sata_adr(dev, handle, pcidevfn, 0, ap, atadev, &dev_adr);
+		if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle)
+			goto out;
+	}
+
+	/* Setting up output buffer */
+	output.length = ACPI_ALLOCATE_BUFFER;
+	output.pointer = NULL;	/* ACPI-CA sets this; save/free it later */
+
+	/* _GTF has no input parameters */
+	err = -EIO;
+	status = acpi_evaluate_object(atadev->obj_handle, "_GTF",
+					NULL, &output);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_DEBUG
+			"%s: Run _GTF error: status = 0x%x\n",
+			__FUNCTION__, status);
+		goto out;
+	}
+
+	if (!output.length || !output.pointer) {
+		printk(KERN_DEBUG
+			"%s: Run _GTF: length or ptr is NULL (0x%llx, 0x%p)\n",
+			__FUNCTION__,
+			(unsigned long long)output.length, output.pointer);
+		acpi_os_free(output.pointer);
+		goto out;
+	}
+
+	out_obj = output.pointer;
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		acpi_os_free(output.pointer);
+		printk(KERN_DEBUG "%s: Run _GTF: error: "
+			"expected object type of ACPI_TYPE_BUFFER, got 0x%x\n",
+			__FUNCTION__, out_obj->type);
+		err = -ENOENT;
+		goto out;
+	}
+
+	atadev->gtf_length = out_obj->buffer.length;
+	atadev->gtf_address = out_obj->buffer.pointer;
+	err = 0;
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_get_GTF);
+
+/**
+ *	taskfile_load_raw - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
+ *
+ *	Outputs ATA taskfile to standard ATA host controller using MMIO
+ *	or PIO as indicated by the ATA_FLAG_MMIO flag.
+ *	Writes the control, feature, nsect, lbal, lbam, and lbah registers.
+ *	Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect,
+ *	hob_lbal, hob_lbam, and hob_lbah.
+ *
+ *	This function waits for idle (!BUSY and !DRQ) after writing
+ *	registers.  If the control register has a new value, this
+ *	function also waits for idle after writing control and before
+ *	writing the remaining registers.
+ *
+ *	LOCKING: TBD:
+ *	Inherited from caller.
+ */
+static void taskfile_load_raw(struct ata_port *ap,
+				struct ata_device *atadev,
+				const struct taskfile_array *gtf)
+{
+	DEBPRINT("(0x1f1-1f7): hex: %02x %02x %02x %02x %02x %02x %02x\n",
+		gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
+		gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
+
+	if (ap->ops->tf_load && ap->ops->exec_command) {
+		struct ata_taskfile atf;
+
+		/* convert gtf to atf */
+		ata_tf_init(ap, &atf, 0);
+		atf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+		atf.protocol = ATA_PROT_NODATA; /* or ATA_PROT_ATAPI_NODATA */
+		atf.feature = gtf->tfa[0];	/* 0x1f1 */
+		atf.nsect   = gtf->tfa[1];	/* 0x1f2 */
+		atf.lbal    = gtf->tfa[2];	/* 0x1f3 */

drivers/scsi/ata_acpi.c line 388

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+		atf.lbam    = gtf->tfa[3];	/* 0x1f4 */
+		atf.lbah    = gtf->tfa[4];	/* 0x1f5 */
+		atf.device  = gtf->tfa[5];	/* 0x1f6 */
+		atf.command = gtf->tfa[6];	/* 0x1f7 */
+
+		DEBPRINT("call tf_load:\n");
+		ap->ops->tf_load(ap, &atf);
+		DEBPRINT("call exec_command:\n");
+		ap->ops->exec_command(ap, &atf);
+		DEBPRINT("tf_load & exec_command done.\n");

delete this entire code block

Quoted 26 lines
drivers/scsi/ata_acpi.c new file +26 -0
+	} else if (ap->ops->qc_issue && ap->ops->qc_prep) {
+		int ret;
+		struct ata_queued_cmd *qc;
+		unsigned long flags;
+		DECLARE_COMPLETION(wait);
+
+		qc = ata_qc_new_init(ap, atadev);
+		if (!qc) {
+			printk(KERN_DEBUG "%s: ata_qc_new_init ret. NULL\n",
+				__FUNCTION__);
+			return;
+		}
+
+		/* convert gtf to qc.tf */
+		qc->tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+		qc->tf.protocol = ATA_PROT_NODATA; /* or ATA_PROT_ATAPI_NODATA */
+		qc->tf.feature = gtf->tfa[0];	/* 0x1f1 */
+		qc->tf.nsect   = gtf->tfa[1];	/* 0x1f2 */
+		qc->tf.lbal    = gtf->tfa[2];	/* 0x1f3 */
+		qc->tf.lbam    = gtf->tfa[3];	/* 0x1f4 */
+		qc->tf.lbah    = gtf->tfa[4];	/* 0x1f5 */
+		qc->tf.device  = gtf->tfa[5];	/* 0x1f6 */
+		qc->tf.command = gtf->tfa[6];	/* 0x1f7 */
+
+		qc->waiting = &wait;
+		qc->complete_fn = ata_qc_complete_noop;

drivers/scsi/ata_acpi.c line 424

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+
+		// TBD: fix locking and return value/wait_for_completion here:
+		DEBPRINT("call ata_qc_issue:\n");
+		spin_lock_irqsave(&ap->host_set->lock, flags);
+		ret = ata_qc_issue(qc);
+		spin_unlock_irqrestore(&ap->host_set->lock, flags);
+		DEBPRINT("ata_qc_issue done: ret = 0x%x\n", ret);
+
+		if (!ret)
+			wait_for_completion(&wait);

always use this code block.

note that 'upstream' branch has Tejun's ata_exec_internal() changes, which cause Jens' suspend patch as well as the above code to change.

Quoted 35 lines
drivers/scsi/ata_acpi.c new file +35 -0
+	} else
+		printk(KERN_WARNING "%s: SATA driver is missing tf_load/exec_command or qc_issue function entry points\n",
+			__FUNCTION__);
+}
+
+/**
+ * do_drive_set_taskfiles - write the drive taskfile settings from _GTF
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * Write {atadev->gtf_address, length atadev->gtf_length} in groups of
+ * REGS_PER_GTF bytes.
+ *
+ */
+int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev)
+{
+	int				err = -ENODEV;
+	int				gtf_count =
+					atadev->gtf_length / REGS_PER_GTF;
+	int				ix;
+	struct taskfile_array		*gtf;
+
+	if (!ata_dev_present(atadev))	/* or port disabled ? */
+		goto out;
+	if (!gtf_count)		/* shouldn't be here */
+		goto out;
+
+	DEBPRINT("total GTF bytes = %lld (0x%llx), gtf_count = %d\n",
+		(unsigned long long)atadev->gtf_length,
+		(unsigned long long)atadev->gtf_length, gtf_count);
+	if (atadev->gtf_length % REGS_PER_GTF) {
+		printk(KERN_ERR "%s: unexpected GTF length (%zd)\n",
+			__FUNCTION__, atadev->gtf_length);

drivers/scsi/ata_acpi.c line 469

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+		goto out;
+	}
+
+	for (ix = 0; ix < gtf_count; ix++) {
+		gtf = (struct taskfile_array *)
+			(atadev->gtf_address + ix * REGS_PER_GTF);
+
+		/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
+		taskfile_load_raw(ap, atadev, gtf);
+	}

stop the loop on error

Quoted 11 lines
drivers/scsi/ata_acpi.c new file +11 -0
+	err = 0;
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_set_taskfiles);
+
+/**
+ * do_drive_update_taskfiles - get then write drive taskfile settings
+ * @ap: the ata_port for the drive
+ *
+ * This applies to both PATA and SATA drives.

drivers/scsi/ata_acpi.c line 491

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +10 -0
+ */
+int do_drive_update_taskfiles(struct ata_port *ap)
+{
+	int ix;
+	int ret;
+
+	for (ix = 0; ix < ATA_MAX_DEVICES; ix++) {
+		ret = do_drive_get_GTF(ap, &ap->device[ix]);
+		if (ret >= 0)
+			ret = do_drive_set_taskfiles(ap, &ap->device[ix]);

what's the point of continuing to loop, but simply avoiding the call, on error? why not just stop the loop?

Quoted 10 lines
drivers/scsi/ata_acpi.c new file +5 -1
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(do_drive_update_taskfiles);
 
 
 
---
#15 lore raw

PATCH 7/7 SATA ACPI debug-only output

In reply to a message not in the archive

From: Randy Dunlap <[email protected]>

Add some extra debug-only output.

---
 drivers/scsi/ata_acpi.c    |   17 ++++++++++++-----
 drivers/scsi/libata-core.c |    6 +++---
 2 files changed, 15 insertions(+), 8 deletions(-)
drivers/scsi/ata_acpi.c +12 -5
@@ -183,9 +183,10 @@ static int get_sata_adr(struct device *d
 	winfo->drivenum = drive;
 
 	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
-	if (ACPI_FAILURE(status))
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("acpi_get_devices failed\n");
 		err = -ENODEV;
-	else {
+	} else {
 		*dev_adr = winfo->sata_adr;
 		atadev->obj_handle = winfo->obj_handle;
 		err = 0;
@@ -219,19 +220,25 @@ int do_drive_SDD(struct ata_port *ap, un
 		__FUNCTION__, ap->id, ix, ap->port_no, ap->hard_port_no);
 
 	/* Don't continue if not a SATA device. */
-	if (!ata_id_is_sata(atadev->id))
+	if (!ata_id_is_sata(atadev->id)) {
+		DEBPRINT("ata_id_is_sata is False\n");
 		goto out;
+	}
 
 	/* Don't continue if device has no _ADR method.
 	 * _SDD is intended for known motherboard devices. */
 	err = pci_acpi_get_dev_handle(dev, &handle, &pcidevfn);
-	if (err < 0)
+	if (err < 0) {
+		DEBPRINT("get_dev_handle failed\n");
 		goto out;
+	}
 	/* Get this drive's _ADR info. */
 	dev_adr = SATA_ADR_RSVD;
 	err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev, &dev_adr);
-	if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle)
+	if (err < 0 || dev_adr == SATA_ADR_RSVD || !atadev->obj_handle) {
+		DEBPRINT("get_sata_adr failed\n");
 		goto out;
+	}
 
 	/* Give the drive Identify data to the drive via the _SDD method */
 	/* _SDD: set up input parameters */
drivers/scsi/libata-core.c +3 -3
@@ -1185,11 +1185,11 @@ retry:
 
 	/* print device capabilities */
 	printk(KERN_DEBUG "ata%u: dev %u cfg "
-	       "49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n",
-	       ap->id, device, dev->id[49],
+	       "00:%04x 49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x 93:%04x\n",
+	       ap->id, device, dev->id[0], dev->id[49],
 	       dev->id[82], dev->id[83], dev->id[84],
 	       dev->id[85], dev->id[86], dev->id[87],
-	       dev->id[88]);
+	       dev->id[88], dev->id[93]);
 
 	/*
 	 * common ATA, ATAPI feature tests

---

#16 lore raw

Re: [PATCH 7/7] SATA ACPI debug-only output

Quoted 13 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add some extra debug-only output.

---
 drivers/scsi/ata_acpi.c    |   17 ++++++++++++-----
 drivers/scsi/libata-core.c |    6 +++---
 2 files changed, 15 insertions(+), 8 deletions(-)

drivers/scsi/ata_acpi.c static int get_sata_adr(struct device *d · line 187

Quoted 6 lines
drivers/scsi/ata_acpi.c +2 -1
@@ -183,9 +183,10 @@ static int get_sata_adr(struct device *d
 	winfo->drivenum = drive;
 
 	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
-	if (ACPI_FAILURE(status))
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("acpi_get_devices failed\n");

don't invent your own debugprint, use libata's

#17 lore raw

Re: [PATCH 7/7] SATA ACPI debug-only output

Quoted 6 lines · On Wed, 14 Dec 2005 17:23:58 -0500 Jeff Garzik <[email protected]> wrote:
Quoted 5 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add some extra debug-only output.

Quoted 6 lines

drivers/scsi/ata_acpi.c static int get_sata_adr(struct device *d · line 187

Quoted 4 lines
drivers/scsi/ata_acpi.c +2 -1
 	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
-	if (ACPI_FAILURE(status))
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("acpi_get_devices failed\n");

don't invent your own debugprint, use libata's

DPRINTK() is too verbose for me. Would you accept something that just does debug printing for probe/discovery/enumeration and not on every I/O? then I could use the probe/discovery/enumeration level of debug printing.

---
~Randy

#18 lore raw

Re: [PATCH 7/7] SATA ACPI debug-only output

Quoted 19 lines · Randy Dunlap
Quoted 7 lines · On Wed, 14 Dec 2005 17:23:58 -0500 Jeff Garzik <[email protected]> wrote:
Quoted 5 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add some extra debug-only output.

Quoted 6 lines

drivers/scsi/ata_acpi.c static int get_sata_adr(struct device *d · line 187

Quoted 4 lines
	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
drivers/scsi/ata_acpi.c +2 -1
-	if (ACPI_FAILURE(status))
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("acpi_get_devices failed\n");

don't invent your own debugprint, use libata's

Quoted 4 lines

DPRINTK() is too verbose for me. Would you accept something that just does debug printing for probe/discovery/enumeration and not on every I/O? then I could use the probe/discovery/enumeration level of debug printing.

Dig up the patches from Borislav Petkov on linux-ide that made debug messages more fine-grained. Then we can specific things on and off at runtime...

Jeff

#19 lore raw

Re: [PATCH 7/7] SATA ACPI debug-only output

Quoted 20 lines · On Wed, 14 Dec 2005 17:42:00 -0500 Jeff Garzik <[email protected]> wrote:
Quoted 19 lines · Randy Dunlap
Quoted 13 lines · On Wed, 14 Dec 2005 17:23:58 -0500 Jeff Garzik <[email protected]> wrote:

drivers/scsi/ata_acpi.c static int get_sata_adr(struct device *d · line 187

Quoted 9 lines · Randy Dunlap

From: Randy Dunlap <[email protected]>

Add some extra debug-only output.

	status = acpi_get_devices(NULL, get_devices, winfo, NULL);
drivers/scsi/ata_acpi.c +2 -1
-	if (ACPI_FAILURE(status))
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("acpi_get_devices failed\n");

don't invent your own debugprint, use libata's

Quoted 8 lines
Quoted 4 lines

DPRINTK() is too verbose for me. Would you accept something that just does debug printing for probe/discovery/enumeration and not on every I/O? then I could use the probe/discovery/enumeration level of debug printing.

Dig up the patches from Borislav Petkov on linux-ide that made debug messages more fine-grained. Then we can specific things on and off at runtime...

OK, I found 1/2 in the archive but not 2/2. Borislav, did you send 2/2? Can you resend it, please?

---
~Randy