[PATCH] ACPI support for IDE drives

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

This patch is adds ACPI support for IDE drives. It is basically a rework
of Randy Dunlaps patch for libata. And as such it has exactly the same
issues (namely it breaks layering).

But, however, there are machines out there who do require ACPI support
even for standard IDE controllers. So we need to implement something
here; just telling the users "we're not doing it" is hardly gaining us
anything.
And I'm told that Ubuntu has something like this for ages; but of course
this one is far superior :-).

As usual, comments etc. welcome.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke			hare@xxxxxxx
SuSE Linux Products GmbH		S390 & zSeries
Maxfeldstraße 5				+49 911 74053 688
90409 Nürnberg				http://www.suse.de
IDE: Add ACPI support

This patch adds ACPI support for IDE drives. It is based on Randy Dunlaps 
earlier patch for libata with some modifications to make it work with generic
IDE drives.

Signed-off-by: Hannes Reinecke <hare@xxxxxxx>

--- linux-2.6.15/drivers/ide/ide-acpi.c.orig	2006-03-03 14:08:24.000000000 +0100
+++ linux-2.6.15/drivers/ide/ide-acpi.c	2006-03-06 12:24:06.356874120 +0100
@@ -0,0 +1,680 @@
+/*
+ * ide-acpi.c
+ * Provides ACPI support for IDE drives.
+ *
+ * Copyright (C) 2005 Intel Corp.
+ * Copyright (C) 2005 Randy Dunlap
+ * Copyright (C) 2006 SUSE Linux Products GmbH
+ * Copyright (C) 2006 Hannes Reinecke
+ */
+
+#include <linux/ata.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <acpi/acpi.h>
+#include <linux/ide.h>
+#include <linux/pci.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 REGS_PER_GTF		7
+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 ide_acpi_drive_link {
+	ide_drive_t	*drive;
+	acpi_handle	 obj_handle;
+	u8		 idbuff[512];
+};
+
+struct ide_acpi_hwif_link {
+	ide_hwif_t			*hwif;
+	acpi_handle			 obj_handle;
+	struct GTM_buffer		 gtm;
+	struct ide_acpi_drive_link	 master;
+	struct ide_acpi_drive_link	 slave;
+};
+	
+#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 */
+
+extern int ide_noacpi;
+extern int ide_noacpitfs;
+
+/**
+ * pata_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 the ACPI object handle to the corresponding PCI device.
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int pata_get_dev_handle(struct device *dev, acpi_handle *handle,
+			       acpi_integer *pcidevfn)
+{
+	unsigned int domain, bus, devnum, func;
+	acpi_integer addr;
+	acpi_handle dev_handle, parent_handle;
+	int scanned;
+	struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER,
+					.pointer = NULL};
+	acpi_status status;
+	struct acpi_device_info	*dinfo = NULL;
+	int ret = -ENODEV;
+
+	DEBPRINT("ENTER: dev->bus_id='%s'\n", dev->bus_id);
+
+	if ((scanned = sscanf(dev->bus_id, "%x:%x:%x.%x",
+			&domain, &bus, &devnum, &func)) != 4) {
+		DEBPRINT("sscanf ret. %d\n", scanned);
+		goto err;
+	}
+
+	dev_handle = DEVICE_ACPI_HANDLE(dev);
+	parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+
+	status = acpi_get_object_info(parent_handle, &buffer);
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("get_object_info for parent failed\n");
+		goto err;
+	}
+	dinfo = buffer.pointer;
+	if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+	    dinfo->address == bus) {
+		/* ACPI spec for _ADR for PCI bus: */
+		addr = (acpi_integer)(devnum << 16 | func);
+		*pcidevfn = addr;
+		*handle = dev_handle;
+	} else {
+		DEBPRINT("get_object_info for parent has wrong "
+			" bus: %llu, should be %d\n",
+			dinfo ? (unsigned long long)dinfo->address : -1ULL,
+			bus);
+		goto err;
+	}
+
+	DEBPRINT("for dev=0x%x.%x, addr=0x%llx, parent=0x%p, *handle=0x%p\n",
+		 devnum, func, (unsigned long long)addr,
+		 dev->parent, *handle);
+	if (!*handle)
+		goto err;
+	ret = 0;
+err:
+	acpi_os_free(dinfo);
+	return ret;
+}
+
+/**
+ * ide_acpi_hwif_get_handle - Get ACPI object handle for a given hwif
+ * @hwif: device to locate
+ *
+ * Retrieves the object handle of a given hwif. According to the ACPI
+ * spec is the hwif a child of the PCI device.
+ *
+ * Returns handle on success, 0 on error.
+ */
+acpi_handle ide_acpi_hwif_get_handle(ide_hwif_t *hwif)
+{
+	struct device		*dev = hwif->gendev.parent;
+	acpi_handle		dev_handle;
+	acpi_integer		pcidevfn;
+	acpi_handle		chan_handle;
+	int			err;
+
+	DEBPRINT("ENTER: device %s\n", hwif->name);
+
+	if (!dev) {
+		DEBPRINT("no PCI device for %s\n", hwif->name);
+		return NULL;
+	}
+
+	err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+	if (err < 0) {
+		DEBPRINT("pata_get_dev_handle failed (%d)\n", err);
+		return NULL;
+	}
+
+	/* get child objects of dev_handle == channel objects,
+	 * + _their_ children == drive objects */
+	/* channel is ap->hard_port_no */
+	chan_handle = acpi_get_child(dev_handle, hwif->channel);
+	DEBPRINT("chan adr=%d: handle=0x%p\n",
+		 hwif->channel, chan_handle);
+
+	return chan_handle;
+}
+
+/**
+ * ide_acpi_drive_get_handle - Get ACPI object handle for a given drive
+ * @drive: device to locate
+ *
+ * Retrieves the object handle of a given drive. According to the ACPI
+ * spec the drive is a child of the hwif.
+ *
+ * Returns handle on success, 0 on error.
+ */
+acpi_handle ide_acpi_drive_get_handle(ide_drive_t *drive)
+{
+	ide_hwif_t	*hwif = HWIF(drive);
+	int		 port;
+	acpi_handle	 drive_handle;
+
+	if (!hwif->acpidata)
+		return NULL;
+
+	if (!hwif->acpidata->obj_handle)
+		return NULL;
+
+	port = hwif->channel ? drive->dn - 2: drive->dn;
+
+	DEBPRINT("ENTER: %s at channel#: %d port#: %d\n",
+		 drive->name, hwif->channel, port);
+
+
+	/* TBD: could also check ACPI object VALID bits */
+	drive_handle = acpi_get_child(hwif->acpidata->obj_handle, port);
+	DEBPRINT("drive %s handle 0x%p\n", drive->name, drive_handle);
+
+	return drive_handle;
+}
+
+/**
+ * do_drive_get_GTF - get the drive bootup default taskfile settings
+ * @drive: the drive for which the taskfile settings should be retrieved
+ * @gtf_length: number of bytes of _GTF data returned at @gtf_address
+ * @gtf_address: buffer containing _GTF taskfile arrays
+ *
+ * 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.
+ *
+ * The returned @gtf_length and @gtf_address are only valid if the
+ * function return value is 0.
+ */
+int do_drive_get_GTF(ide_drive_t *drive,
+		     unsigned int *gtf_length, unsigned long *gtf_address,
+		     unsigned long *obj_loc)
+{
+	acpi_status			status;
+	struct acpi_buffer		output;
+	union acpi_object 		*out_obj;
+	ide_hwif_t			*hwif = HWIF(drive);
+	struct device			*dev = hwif->gendev.parent;
+	int				err = -ENODEV;
+	int				port;
+
+	*gtf_length = 0;
+	*gtf_address = 0UL;
+	*obj_loc = 0UL;
+
+	if (ide_noacpi)
+		return 0;
+
+	if (!dev) {
+		DEBPRINT("no PCI device for %s\n", hwif->name);
+		goto out;
+	}
+
+	if (!hwif->acpidata) {
+		DEBPRINT("no ACPI data for %s\n", hwif->name);
+		goto out;
+	}
+
+	port = hwif->channel ? drive->dn - 2: drive->dn;
+
+	if (!drive->acpidata) {
+		if (port == 0) {
+			drive->acpidata = &hwif->acpidata->master;
+			hwif->acpidata->master.drive = drive;
+		} else {
+			drive->acpidata = &hwif->acpidata->slave;
+			hwif->acpidata->slave.drive = drive;
+		}
+	}
+
+	DEBPRINT("ENTER: %s at %s, port#: %d, hard_port#: %d\n",
+		 hwif->name, dev->bus_id, port, hwif->channel);
+
+	if (!drive->present) {
+		DEBPRINT("%s drive %d:%d not present\n",
+			 hwif->name, hwif->channel, port);
+		goto out;
+	}
+
+	/* Get this drive's _ADR info. if not already known. */
+	if (!drive->acpidata->obj_handle) {
+		drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive);
+		if (!drive->acpidata->obj_handle) {
+			DEBPRINT("No ACPI object found for %s\n",
+				 drive->name);
+			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(drive->acpidata->obj_handle, "_GTF",
+				      NULL, &output);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_DEBUG
+		       "%s: Run _GTF error: status = 0x%x\n",
+		       __FUNCTION__, status);
+		goto out_free;
+	}
+
+	if (!output.length || !output.pointer) {
+		DEBPRINT("Run _GTF: "
+		       "length or ptr is NULL (0x%llx, 0x%p)\n",
+		       (unsigned long long)output.length,
+		       output.pointer);
+		goto out_free;
+	}
+
+	out_obj = output.pointer;
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		DEBPRINT("Run _GTF: error: "
+		       "expected object type of ACPI_TYPE_BUFFER, "
+		       "got 0x%x\n", out_obj->type);
+		err = -ENOENT;
+		goto out_free;
+	}
+
+	if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+	    out_obj->buffer.length % REGS_PER_GTF) {
+		printk(KERN_ERR
+		       "%s: unexpected GTF length (%d) or addr (0x%p)\n",
+		       __FUNCTION__, out_obj->buffer.length,
+		       out_obj->buffer.pointer);
+		err = -ENOENT;
+		goto out_free;
+	}
+
+	*gtf_length = out_obj->buffer.length;
+	*gtf_address = (unsigned long)out_obj->buffer.pointer;
+	*obj_loc = (unsigned long)out_obj;
+	DEBPRINT("returning gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n",
+		 *gtf_length, *gtf_address, *obj_loc);
+	err = 0;
+out_free:
+	acpi_os_free(output.pointer);
+out:
+	return err;
+}
+
+/**
+ * taskfile_load_raw - send taskfile registers to drive
+ * @drive: drive to which output is sent
+ * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
+ *
+ * Outputs IDE taskfile to the drive.
+ */
+static int taskfile_load_raw(ide_drive_t *drive,
+			      const struct taskfile_array *gtf)
+{
+	ide_task_t args;
+	int err = 0;
+
+	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]);
+
+	memset(&args, 0, sizeof(ide_task_t));
+	args.command_type = IDE_DRIVE_TASK_NO_DATA;
+	args.data_phase   = TASKFILE_IN;
+	args.handler      = &task_no_data_intr;
+
+	/* convert gtf to IDE Taskfile */
+	args.tfRegister[1] = gtf->tfa[0];	/* 0x1f1 */
+	args.tfRegister[2] = gtf->tfa[1];	/* 0x1f2 */
+	args.tfRegister[3] = gtf->tfa[2];	/* 0x1f3 */
+	args.tfRegister[4] = gtf->tfa[3];	/* 0x1f4 */
+	args.tfRegister[5] = gtf->tfa[4];	/* 0x1f5 */
+	args.tfRegister[6] = gtf->tfa[5];	/* 0x1f6 */
+	args.tfRegister[7] = gtf->tfa[6];	/* 0x1f7 */
+
+	if (ide_noacpitfs)
+		return err;
+
+	err = ide_raw_taskfile(drive, &args, NULL);
+	if (err)
+		printk(KERN_ERR "%s: ide_raw_taskfile failed: %u\n",
+		       __FUNCTION__, err);
+
+	return err;
+}
+
+/**
+ * do_drive_set_taskfiles - write the drive taskfile settings from _GTF
+ * @drive: the drive to which the taskfile command should be sent
+ * @gtf_length: total number of bytes of _GTF taskfiles
+ * @gtf_address: location of _GTF taskfile arrays
+ *
+ * Write {gtf_address, length gtf_length} in groups of
+ * REGS_PER_GTF bytes.
+ */
+static int do_drive_set_taskfiles(ide_drive_t *drive,
+				  unsigned int gtf_length,
+				  unsigned long gtf_address)
+{
+	int			err = -ENODEV;
+	int			gtf_count = gtf_length / REGS_PER_GTF;
+	int			ix;
+	struct taskfile_array	*gtf;
+
+	if (ide_noacpi)
+		return 0;
+
+	DEBPRINT("ENTER: %s, hard_port#: %d\n", drive->name, drive->dn);
+
+	if (!drive->present)
+		goto out;
+	if (!gtf_count)		/* shouldn't be here */
+		goto out;
+
+	DEBPRINT("total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n",
+		 gtf_length, gtf_length, gtf_count, gtf_address);
+
+	if (gtf_length % REGS_PER_GTF) {
+		printk(KERN_ERR "%s: unexpected GTF length (%d)\n",
+		       __FUNCTION__, gtf_length);
+		goto out;
+	}
+
+	for (ix = 0; ix < gtf_count; ix++) {
+		gtf = (struct taskfile_array *)
+			(gtf_address + ix * REGS_PER_GTF);
+
+		/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
+		err = taskfile_load_raw(drive, gtf);
+		if (err)
+			break;
+	}
+
+out:
+	return err;
+}
+
+/**
+ * ide_acpi_exec_tfs - get then write drive taskfile settings
+ * @drive: the drive for which the taskfile settings should be
+ *         written.
+ *
+ * According to the ACPI spec this should be called after _STM
+ * has been evaluated for the interface. Some ACPI vendors interpret
+ * that as a hard requirement and modify the taskfile according
+ * to the Identify Drive information passed down with _STM.
+ * So one should really make sure to call this only after _STM has
+ * been executed.
+ */
+int ide_acpi_exec_tfs(ide_drive_t *drive)
+{
+	int		ret;
+	unsigned int	gtf_length;
+	unsigned long	gtf_address;
+	unsigned long	obj_loc;
+
+	if (ide_noacpi)
+		return 0;
+
+	DEBPRINT("call get_GTF, drive=%s port=%d\n", drive->name, drive->dn);
+
+	ret = do_drive_get_GTF(drive, &gtf_length, &gtf_address, &obj_loc);
+	if (ret < 0) {
+		DEBPRINT("get_GTF error (%d)\n", ret);
+		return ret;
+	}
+
+	DEBPRINT("call set_taskfiles, drive=%s\n", drive->name);
+
+	ret = do_drive_set_taskfiles(drive, gtf_length, gtf_address);
+	acpi_os_free((void *)obj_loc);
+	if (ret < 0) {
+		DEBPRINT("set_taskfiles error (%d)\n", ret);
+	}
+
+	DEBPRINT("ret=%d\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ide_acpi_exec_tfs);
+
+/**
+ * ide_acpi_get_timing - get the channel (controller) timings
+ * @hwif: target IDE interface (channel)
+ *
+ * This function executes the _GTM ACPI method for the target channel.
+ *
+ */
+void ide_acpi_get_timing(ide_hwif_t *hwif)
+{
+	acpi_status		status;
+	struct acpi_buffer	output;
+	union acpi_object 	*out_obj;
+
+	if (ide_noacpi)
+		return;
+
+	DEBPRINT("ENTER:\n");
+
+	if (!hwif->acpidata) {
+		DEBPRINT("no ACPI data for %s\n", hwif->name);
+		return;
+	}
+
+	/* 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(hwif->acpidata->obj_handle, "_GTM",
+				      NULL, &output);
+
+	DEBPRINT("_GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n",
+		 status, output.pointer,
+		 (unsigned long long)output.length);
+
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("Run _GTM error: status = 0x%x\n", status);
+		return;
+	}
+
+	if (!output.length || !output.pointer) {
+		DEBPRINT("Run _GTM: length or ptr is NULL (0x%llx, 0x%p)\n",
+		       (unsigned long long)output.length,
+		       output.pointer);
+		acpi_os_free(output.pointer);
+		return;
+	}
+
+	out_obj = output.pointer;
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		acpi_os_free(output.pointer);
+		DEBPRINT("Run _GTM: error: "
+		       "expected object type of ACPI_TYPE_BUFFER, "
+		       "got 0x%x\n", out_obj->type);
+		return;
+	}
+
+	if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+	    out_obj->buffer.length != sizeof(struct GTM_buffer)) {
+		acpi_os_free(output.pointer);
+		printk(KERN_ERR
+		       "%s: unexpected _GTM length (0x%x)[should be 0x%x] or addr (0x%p)\n",
+		       __FUNCTION__, out_obj->buffer.length,
+		       sizeof(struct GTM_buffer), out_obj->buffer.pointer);
+		return;
+	}
+
+	memcpy(&hwif->acpidata->gtm, out_obj->buffer.pointer,
+	       sizeof(struct GTM_buffer));
+
+	DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx\n",
+		 out_obj->buffer.pointer, out_obj->buffer.length, 
+		 sizeof(struct GTM_buffer));
+
+	DEBPRINT("_GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+		 hwif->acpidata->gtm.PIO_speed0,
+		 hwif->acpidata->gtm.DMA_speed0,
+		 hwif->acpidata->gtm.PIO_speed1,
+		 hwif->acpidata->gtm.DMA_speed1,
+		 hwif->acpidata->gtm.GTM_flags);
+
+	acpi_os_free(output.pointer);
+}
+EXPORT_SYMBOL_GPL(ide_acpi_get_timing);
+
+/**
+ * ide_acpi_push_timing - 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 ide_acpi_push_timing(ide_hwif_t *hwif)
+{
+	int			err;
+	acpi_status		status;
+	struct acpi_object_list	input;
+	union acpi_object 	in_params[3];
+	struct ide_acpi_drive_link	*master = &hwif->acpidata->master;
+	struct ide_acpi_drive_link	*slave = &hwif->acpidata->slave;
+
+	if (ide_noacpi)
+		return;
+
+	DEBPRINT("ENTER:\n");
+
+	if (!hwif->acpidata) {
+		DEBPRINT("no ACPI data for %s\n", hwif->name);
+		return;
+	}
+
+	if (!master->drive)
+		master->drive = &hwif->drives[0];
+
+	if (!slave->drive)
+		slave->drive = &hwif->drives[1];
+
+	if (master->drive->present) {
+		err = taskfile_lib_get_identify(master->drive, master->idbuff);
+		if (err) {
+			DEBPRINT("identify device %s failed (%d)\n",
+				 master->drive->name, err);
+			return;
+		}
+	}
+		
+	if (slave->drive->present) {
+		err = taskfile_lib_get_identify(slave->drive, slave->idbuff);
+		if (err) {
+			DEBPRINT("identify device %s failed (%d)\n",
+				 slave->drive->name, err);
+			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 *)&hwif->acpidata->gtm;
+	in_params[1].type = ACPI_TYPE_BUFFER;
+	in_params[1].buffer.length = sizeof(struct hd_driveid);
+	in_params[1].buffer.pointer = (u8 *)&master->idbuff;
+	in_params[2].type = ACPI_TYPE_BUFFER;
+	in_params[2].buffer.length = sizeof(struct hd_driveid);
+	in_params[2].buffer.pointer = (u8 *)&slave->idbuff;
+	/* Output buffer: _STM has no output */
+
+	status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_STM",
+				      &input, NULL);
+
+	if (ACPI_FAILURE(status)) {
+		DEBPRINT("Run _STM error: status = 0x%x\n", status);
+	}
+	DEBPRINT("_STM status: %d\n", status);
+}
+EXPORT_SYMBOL_GPL(ide_acpi_push_timing);
+
+void ide_acpi_init(ide_hwif_t *hwif)
+{
+	int unit;
+
+	hwif->acpidata = kzalloc(sizeof(struct ide_acpi_hwif_link), GFP_KERNEL);
+	if (!hwif->acpidata)
+		return;
+
+	hwif->acpidata->obj_handle = ide_acpi_hwif_get_handle(hwif);
+	if (!hwif->acpidata->obj_handle) {
+		DEBPRINT("no ACPI object for %s found\n", hwif->name);
+		kfree(hwif->acpidata);
+		hwif->acpidata = NULL;
+		return;
+	}
+
+	if (hwif->drives[0].present) {
+		hwif->acpidata->master.drive = &hwif->drives[0];
+		hwif->drives[0].acpidata = &hwif->acpidata->master;
+	}
+
+	if (hwif->drives[1].present) {
+		hwif->acpidata->slave.drive = &hwif->drives[1];
+		hwif->drives[1].acpidata = &hwif->acpidata->slave;
+	}
+
+	/*
+	 * ACPI requires us to call _STM on startup
+	 */
+	ide_acpi_get_timing(hwif);
+	ide_acpi_push_timing(hwif);
+
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+
+		if (drive->present) {
+
+			/* Execute ACPI startup code */
+			ide_acpi_exec_tfs(drive);
+		}
+			
+	}
+}
+EXPORT_SYMBOL_GPL(ide_acpi_init);
--- linux-2.6.15/drivers/ide/Makefile.orig	2006-01-03 04:21:10.000000000 +0100
+++ linux-2.6.15/drivers/ide/Makefile	2006-03-03 11:10:30.929469393 +0100
@@ -22,6 +22,7 @@ ide-core-$(CONFIG_BLK_DEV_IDEPCI)	+= set
 ide-core-$(CONFIG_BLK_DEV_IDEDMA)	+= ide-dma.o
 ide-core-$(CONFIG_PROC_FS)		+= ide-proc.o
 ide-core-$(CONFIG_BLK_DEV_IDEPNP)	+= ide-pnp.o
+ide-core-$(CONFIG_BLK_DEV_IDEACPI)	+= ide-acpi.o
 
 # built-in only drivers from arm/
 ide-core-$(CONFIG_IDE_ARM)		+= arm/ide_arm.o
--- linux-2.6.15/drivers/ide/ide-probe.c.orig	2006-03-02 13:07:05.674022475 +0100
+++ linux-2.6.15/drivers/ide/ide-probe.c	2006-03-06 12:11:01.037087319 +0100
@@ -1387,6 +1387,9 @@ static int hwif_init(ide_hwif_t *hwif)
 
 done:
 	init_gendisk(hwif);
+
+	ide_acpi_init(hwif);
+
 	hwif->present = 1;	/* success */
 	return 1;
 
--- linux-2.6.15/drivers/ide/ide.c.orig	2006-03-02 13:07:05.714017794 +0100
+++ linux-2.6.15/drivers/ide/ide.c	2006-03-06 11:48:56.120690401 +0100
@@ -189,6 +189,11 @@ int noautodma = 1;
 
 EXPORT_SYMBOL(noautodma);
 
+#ifdef CONFIG_BLK_DEV_IDEACPI
+int ide_noacpi = 0;
+int ide_noacpitfs = 0;
+#endif
+
 /*
  * This is declared extern in ide.h, for access by other IDE modules:
  */
@@ -1222,10 +1227,15 @@ EXPORT_SYMBOL(system_bus_clock);
 static int generic_ide_suspend(struct device *dev, pm_message_t state)
 {
 	ide_drive_t *drive = dev->driver_data;
+	ide_hwif_t *hwif = HWIF(drive);
 	struct request rq;
 	struct request_pm_state rqpm;
 	ide_task_t args;
 
+	/* Call ACPI _GTM only once */
+	if (!(drive->dn % 2))
+		ide_acpi_get_timing(hwif);	
+
 	memset(&rq, 0, sizeof(rq));
 	memset(&rqpm, 0, sizeof(rqpm));
 	memset(&args, 0, sizeof(args));
@@ -1241,10 +1251,17 @@ static int generic_ide_suspend(struct de
 static int generic_ide_resume(struct device *dev)
 {
 	ide_drive_t *drive = dev->driver_data;
+	ide_hwif_t *hwif = HWIF(drive);
 	struct request rq;
 	struct request_pm_state rqpm;
 	ide_task_t args;
 
+	/* Call ACPI _STM only once */
+	if (!(drive->dn % 2))
+		ide_acpi_push_timing(hwif);	
+
+	ide_acpi_exec_tfs(drive);
+
 	memset(&rq, 0, sizeof(rq));
 	memset(&rqpm, 0, sizeof(rqpm));
 	memset(&args, 0, sizeof(args));
@@ -1545,6 +1562,19 @@ static int __init ide_setup(char *s)
 	}
 #endif /* CONFIG_BLK_DEV_IDEPCI */
 
+#ifdef CONFIG_BLK_DEV_IDEACPI
+	if (!strcmp(s, "ide=noacpi")) {
+		printk(" : Disable IDE ACPI support.\n");
+		ide_noacpi = 1;
+		return 1;
+	}
+	if (!strcmp(s, "ide=nogtf")) {
+		printk(" : Disable IDE ACPI _GTF support.\n");
+		ide_noacpitfs = 1;
+		return 1;
+	}
+#endif /* CONFIG_BLK_DEV_IDEACPI */
+
 	if (!strncmp(s, "idewait=", 8)) {
 		char *dummy;
 		ide_wait_ms = simple_strtol(s+8, &dummy, 10);
--- linux-2.6.15/include/linux/ide.h.orig	2006-03-02 13:07:05.714017794 +0100
+++ linux-2.6.15/include/linux/ide.h	2006-03-06 09:53:46.056810348 +0100
@@ -19,6 +19,7 @@
 #include <linux/device.h>
 #include <linux/pci.h>
 #include <linux/completion.h>
+#include <acpi/acpi.h>
 #include <asm/byteorder.h>
 #include <asm/system.h>
 #include <asm/io.h>
@@ -541,6 +542,11 @@ typedef enum {
 struct ide_driver_s;
 struct ide_settings_s;
 
+#ifdef CONFIG_BLK_DEV_IDEACPI
+struct ide_acpi_drive_link;
+struct ide_acpi_hwif_link;
+#endif
+
 typedef struct ide_drive_s {
 	char		name[4];	/* drive name, such as "hda" */
         char            driver_req[10];	/* requests specific driver */
@@ -637,6 +643,9 @@ typedef struct ide_drive_s {
 
 	int		lun;		/* logical unit */
 	int		crc_count;	/* crc counter to reduce drive speed */
+#ifdef CONFIG_BLK_DEV_IDEACPI
+	struct ide_acpi_drive_link *acpidata;
+#endif
 	struct list_head list;
 	struct device	gendev;
 	struct completion gendev_rel_comp;	/* to deal with device release() */
@@ -802,6 +811,10 @@ typedef struct hwif_s {
 	unsigned dma;
 
 	void (*led_act)(void *data, int rw);
+
+#ifdef CONFIG_BLK_DEV_IDEACPI
+	struct ide_acpi_hwif_link *acpidata;
+#endif
 } ____cacheline_internodealigned_in_smp ide_hwif_t;
 
 /*
@@ -1293,6 +1306,18 @@ static inline void ide_dma_verbose(ide_d
 static inline void ide_release_dma(ide_hwif_t *drive) {;}
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDEACPI
+extern int ide_acpi_exec_tfs(ide_drive_t *drive);
+extern void ide_acpi_get_timing(ide_hwif_t *hwif);
+extern void ide_acpi_push_timing(ide_hwif_t *hwif);
+extern void ide_acpi_init(ide_hwif_t *hwif);
+#else
+static inline int ide_acpi_exec_tfs(ide_drive_t *drive) { return 0; }
+static inline void ide_acpi_get_timing(ide_hwif_t *hwif) { ; }
+static inline void ide_acpi_push_timing(ide_hwif_t *hwif) { ; }
+static inline void ide_acpi_init(ide_hwif_t *hwif) { ; }
+#endif
+
 extern int ide_hwif_request_regions(ide_hwif_t *hwif);
 extern void ide_hwif_release_regions(ide_hwif_t* hwif);
 extern void ide_unregister (unsigned int index);

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux