public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC]add ACPI hooks for IDE suspend/resume
@ 2005-12-06  6:10 Shaohua Li
  2005-12-06 22:20 ` Matthew Garrett
  2005-12-07  0:11 ` Randy.Dunlap
  0 siblings, 2 replies; 25+ messages in thread
From: Shaohua Li @ 2005-12-06  6:10 UTC (permalink / raw)
  To: linux-ide, lkml; +Cc: pavel, Len Brown, Matthew Garrett, akpm

Hi,
Adding ACPI IDE hook in IDE suspend/resume. The ACPI spec
explicitly says we must call some ACPI methods to restore IDE drives.
The sequences defined by ACPI spec are:
suspend:
1. Get the DMA and PIO info from IDE channel's _GTM method.

resume:
1. Calling IDE channel's _STM to set the transfer timing setting.
2. For each drive on the IDE channel, running drive's _GTF to get the
ATA commands required to reinitialize each drive.
3. Sending the ATA commands gotton from step 2 to drives.

TODO: invoking ATA commands.

Though we didn't invoke ATA commands, this patch fixes the bug at 
http://bugzilla.kernel.org/show_bug.cgi?id=5604. And Matthew said this
actually fixes a lot of systems in his test.
I'm not familiar with IDE, so comments/suggestions are welcome.

Thanks,
Shaohua

---

 linux-2.6.15-rc5-root/drivers/ide/ide.c |  282 ++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+)

diff -puN drivers/ide/ide.c~acpi-ide drivers/ide/ide.c
--- linux-2.6.15-rc5/drivers/ide/ide.c~acpi-ide	2005-12-07 03:01:36.000000000 +0800
+++ linux-2.6.15-rc5-root/drivers/ide/ide.c	2005-12-07 03:01:36.000000000 +0800
@@ -155,6 +155,10 @@
 #include <linux/device.h>
 #include <linux/bitops.h>
 
+#ifdef CONFIG_ACPI
+#include <linux/acpi.h>
+#endif
+
 #include <asm/byteorder.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
@@ -1214,6 +1218,279 @@ int system_bus_clock (void)
 
 EXPORT_SYMBOL(system_bus_clock);
 
+#ifdef CONFIG_ACPI
+static int ide_acpi_find_device(struct device *dev, acpi_handle *handle)
+{
+	int i, tmp;
+	acpi_integer addr;
+
+	if (sscanf(dev->bus_id, "%u.%u", &tmp, &i) != 2)
+		return -ENODEV;
+
+	addr = (acpi_integer)i;
+	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}
+
+/* This assumes the ide controller is a PCI device */
+static int ide_acpi_find_channel(struct device *dev, acpi_handle *handle)
+{
+	int num;
+	int channel;
+	acpi_integer addr;
+
+	num = sscanf(dev->bus_id, "ide%x", &channel);
+
+	if (num != 1 || !dev->parent)
+		return -ENODEV;
+	addr = (acpi_integer)channel;
+	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}
+
+static struct acpi_bus_type ide_acpi_bus = {
+	.bus = &ide_bus_type,
+	.find_device = ide_acpi_find_device,
+	.find_bridge = ide_acpi_find_channel,
+};
+
+static int __init ide_acpi_init(void)
+{
+	return register_acpi_bus_type(&ide_acpi_bus);
+}
+
+/* The _GTM return package length is 5 dwords */
+#define GTM_LEN (sizeof(u32) * 5)
+struct acpi_ide_state {
+	acpi_handle handle; /* channel device's handle */
+	u32 gtm[GTM_LEN/sizeof(u32)]; /* info from _GTM */
+	struct hd_driveid id_buff[2]; /* one chanel has two drives */
+	int suspend_drives;
+	int resume_drives;
+};
+
+static void acpi_ide_data_handler(acpi_handle handle,
+	u32 function, void *context)
+{
+	/* nothing to do */
+}
+
+/* acpi data for a chanel */
+static struct acpi_ide_state *ide_alloc_acpi_state(acpi_handle handle)
+{
+	struct acpi_ide_state * state;
+	acpi_status status;
+
+	state = kzalloc(sizeof(struct acpi_ide_state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+	status = acpi_attach_data(handle, acpi_ide_data_handler, state);
+	if (ACPI_FAILURE(status))
+		return NULL;
+	return state;
+}
+
+static struct acpi_ide_state *ide_get_acpi_state(acpi_handle handle)
+{
+	struct acpi_ide_state * state;
+	acpi_status status;
+
+	status = acpi_get_data(handle, acpi_ide_data_handler, (void **)&state);
+	if (ACPI_FAILURE(status))
+		return NULL;
+	return state;
+}
+
+static void ide_free_acpi_state(acpi_handle handle)
+{
+	struct acpi_ide_state *state;
+
+	state = ide_get_acpi_state(handle);
+	acpi_detach_data(handle, acpi_ide_data_handler);
+	kfree(state);
+}
+
+static int acpi_ide_suspend(struct device *dev)
+{
+	acpi_handle handle, parent_handle;
+	struct acpi_ide_state *state;
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *package;
+	ide_drive_t *drive = dev->driver_data;
+	int drive_id = 0;
+
+	handle = DEVICE_ACPI_HANDLE(dev);
+	if (!handle) {
+		printk(KERN_DEBUG "IDE device's ACPI handler is NULL\n");
+		return -ENODEV;
+	}
+	if (ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))) {
+		printk(KERN_ERR "ACPI get parent handler error\n");
+		return -ENODEV;
+	}
+	state = ide_get_acpi_state(parent_handle);
+	if (!state) {
+		state = ide_alloc_acpi_state(parent_handle);
+		if (!state)
+			return -ENODEV;
+	}
+
+	/* invoke _GTM only once */
+	state->suspend_drives++;
+	if (state->suspend_drives > 1) {
+		drive_id = 1;
+		goto id;
+	}
+
+	status = acpi_evaluate_object(parent_handle, "_GTM", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR "Error evaluating _GTM\n");
+		return -ENODEV;
+	}
+	package = (union acpi_object *) buffer.pointer;
+	if (package->buffer.length != GTM_LEN) {
+		printk(KERN_ERR "Buffer length returned by _GTM is wrong\n");
+		acpi_os_free(buffer.pointer);
+		return -ENODEV;
+	}
+	memcpy(state->gtm, package->buffer.pointer, GTM_LEN);
+	state->handle = parent_handle;
+	acpi_os_free(buffer.pointer);
+id:
+	taskfile_lib_get_identify(drive, (u8*)&state->id_buff[drive_id]);
+	return 0;
+}
+
+static int acpi_ide_invoke_stm(struct acpi_ide_state *state)
+{
+	struct acpi_object_list input;
+	union acpi_object params[3];
+	acpi_status status;
+
+	input.count = 3;
+	input.pointer = params;
+	params[0].type = ACPI_TYPE_BUFFER;
+	params[0].buffer.length = sizeof(state->gtm);
+	params[0].buffer.pointer = (char*)state->gtm;
+
+	params[1].type = ACPI_TYPE_BUFFER;
+	params[1].buffer.length = sizeof(state->id_buff[0]);
+	params[1].buffer.pointer = (char *)&state->id_buff[0];
+
+	params[2].type = ACPI_TYPE_BUFFER;
+	params[2].buffer.length = sizeof(state->id_buff[1]);
+	params[2].buffer.pointer = (char *)&state->id_buff[1];
+
+	status = acpi_evaluate_object(state->handle, "_STM", &input, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR "Evaluating _STM error\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int acpi_ide_invoke_gtf(acpi_handle handle, ide_drive_t *drive)
+{
+	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+#if 0
+	ide_task_t args;
+	int index = 0;
+	unsigned char *data;
+#endif
+	union acpi_object *package;
+	acpi_status status;
+
+	status = acpi_evaluate_object(handle, "_GTF", NULL, &output);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR "evaluate _GTF error\n");
+		return -ENODEV;
+	}
+
+	package = (union acpi_object *) output.pointer;
+	if (package->type != ACPI_TYPE_BUFFER
+			|| (package->buffer.length % 7) != 0) {
+		acpi_os_free(output.pointer);
+		printk(KERN_ERR "_GTF returned value is wrong\n");
+		return -ENODEV;
+	}
+#if 0
+	printk(KERN_DEBUG "Start invoking _GTF commands\n");
+
+	data = package->buffer.pointer;
+	/* sumbit ATA commands */
+	while (index < package->buffer.length) {
+		memset(&args, 0, sizeof(ide_task_t));
+		args.tfRegister[IDE_ERROR_OFFSET] = data[index];
+		args.tfRegister[IDE_NSECTOR_OFFSET] = data[index + 1];
+		args.tfRegister[IDE_SECTOR_OFFSET] = data[index + 2];
+		args.tfRegister[IDE_LCYL_OFFSET] = data[index + 3];
+		args.tfRegister[IDE_HCYL_OFFSET] = data[index + 4];
+		args.tfRegister[IDE_SELECT_OFFSET] = data[index + 5];
+		args.tfRegister[IDE_STATUS_OFFSET] = data[index + 6];
+		args.command_type = IDE_DRIVE_TASK_NO_DATA;
+		args.handler = &task_no_data_intr;
+		/* submit command */
+		index += 7;
+	}
+#endif
+	acpi_os_free(output.pointer);
+	return 0;
+}
+
+static int acpi_ide_resume(struct device *dev)
+{
+	acpi_handle handle, parent_handle;
+	struct acpi_ide_state *state;
+	ide_drive_t *drive = dev->driver_data;
+
+	handle = DEVICE_ACPI_HANDLE(dev);
+	if (!handle) {
+		printk(KERN_DEBUG "IDE device's ACPI handler is NULL\n");
+		return -ENODEV;
+	}
+	if (ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))) {
+		printk(KERN_ERR "ACPI get parent handler error\n");
+		return -ENODEV;
+	}
+	state = ide_get_acpi_state(parent_handle);
+	if (state == NULL)
+		return -ENODEV;
+
+	/* invoke _STM only once */
+	state->resume_drives++;
+	if (state->resume_drives == 1) {
+		printk(KERN_DEBUG "Start invoking _STM\n");
+		if (acpi_ide_invoke_stm(state))
+			return -ENODEV;
+	}
+
+	if (state->resume_drives == state->suspend_drives)
+		ide_free_acpi_state(parent_handle);
+	return acpi_ide_invoke_gtf(handle, drive);
+}
+
+#else
+static int __init ide_acpi_init(void)
+{
+	return 0;
+}
+
+static int acpi_ide_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int acpi_ide_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
 static int generic_ide_suspend(struct device *dev, pm_message_t state)
 {
 	ide_drive_t *drive = dev->driver_data;
@@ -1221,6 +1498,8 @@ static int generic_ide_suspend(struct de
 	struct request_pm_state rqpm;
 	ide_task_t args;
 
+	acpi_ide_suspend(dev);
+
 	memset(&rq, 0, sizeof(rq));
 	memset(&rqpm, 0, sizeof(rqpm));
 	memset(&args, 0, sizeof(args));
@@ -1240,6 +1519,8 @@ static int generic_ide_resume(struct dev
 	struct request_pm_state rqpm;
 	ide_task_t args;
 
+	acpi_ide_resume(dev);
+
 	memset(&rq, 0, sizeof(rq));
 	memset(&rqpm, 0, sizeof(rqpm));
 	memset(&args, 0, sizeof(args));
@@ -1923,6 +2204,7 @@ static int __init ide_init(void)
 	system_bus_speed = ide_system_bus_speed();
 
 	bus_register(&ide_bus_type);
+	ide_acpi_init();
 
 	init_ide_data();
 
_



^ permalink raw reply	[flat|nested] 25+ messages in thread
* RE: [RFC]add ACPI hooks for IDE suspend/resume
@ 2005-12-07 21:58 Li, Shaohua
  0 siblings, 0 replies; 25+ messages in thread
From: Li, Shaohua @ 2005-12-07 21:58 UTC (permalink / raw)
  To: Bartlomiej Zolnierkiewicz
  Cc: Alan Cox, Matthew Garrett, linux-ide, lkml, pavel, Brown, Len,
	akpm

Hi,
>
>On 12/7/05, Shaohua Li <shaohua.li@intel.com> wrote:
>> On Wed, 2005-12-07 at 16:49 +0100, Bartlomiej Zolnierkiewicz wrote:
>> > On 12/7/05, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
>> > > On Mer, 2005-12-07 at 15:45 +0100, Bartlomiej Zolnierkiewicz
wrote:
>> > > > OK, I understand it now - when using 'ide-generic' host driver
for
>IDE
>> > > > PCI device, resume fails (for obvious reason - IDE PCI device
is
>not
>> > > > re-configured) and this patch fixes it through using ACPI
methods.
>> >
>> > I was talking about bugzilla bug #5604.
>> Sorry for my ignorance in IDE side. From the ACPI spec, there isn't a
>> generic way to save/restore IDE's configuration. That's why ACPI
>> provides such methods. I suppose all IDE drivers need call the
methods,
>> wrong?
>
>From the hardware POV:
>* there is generic way to save/restores IDE device's configuration
>* there is no generic way to save/restore IDE controller's
configuration
>
>From the software POV what we only do currently is setting controller
>and drive for a correct transfer mode by using host driver specific
>callback
>(in case of using 'ide-generic' there is no such callback).
>
>> > > Even in the piix case some devices need it because the bios wants
to
>> > > issue commands such as password control if the laptop is set up
in
>> > > secure modes.
>> >
>> > I completely agree.  However at the moment this patch doesn't seem
>> > to issue any ATA commands (code is commented out in _GTF) so
>> > this is not a case for bugzilla bug #5604.
>> I actually tried to invoke ATA commands using IDE APIs, but can't
find
>> any available one. I'd be very happy if you can give me any hint how
to
>> do it or even you can fix it.
>
>Probably do_rw_taskfile() is the method you want to use, you also need
>to place invoking of ACPI provided ATA commands in the right place in
>the IDE PM state machine [ ide_{start,complete}_power_step() ].
>
>PS1 Please don't use taskfile_lib_get_identify(), drive->id
>should contain valid ID - if it doesn't it is a BUG.
>
>PS2 Have you seen libata ACPI patches by Randy?
>Maybe some of the code dealing with ACPI can be put to
><linux/ata.h> and be shared between IDE and libata drivers?
Thanks a lot! I'll try your suggestions after my travel. Hopefully I can
understand the IDE staffs you mentioned then :).

Thanks,
Shaohua

^ permalink raw reply	[flat|nested] 25+ messages in thread
* RE: [RFC]add ACPI hooks for IDE suspend/resume
@ 2005-12-07 23:19 Li, Shaohua
  0 siblings, 0 replies; 25+ messages in thread
From: Li, Shaohua @ 2005-12-07 23:19 UTC (permalink / raw)
  To: Matthew Garrett, Pavel Machek
  Cc: Alan Cox, Bartlomiej Zolnierkiewicz, linux-ide, lkml, Brown, Len,
	akpm

Hi,
>
>On Wed, Dec 07, 2005 at 11:43:23PM +0100, Pavel Machek wrote:
>
>> If someone swapped the drive while runtime, it would not be true,
too,
>> I guess. But that would be stupid thing to do. Swapping drive during
>> suspend-to-RAM would be similary stupid. During suspend-to-disk, it
>> might theoretically work, but we have big warnings saying "don't do
>> that".
>
>ACPI systems tend to fire ACPI notifications when a drive is ejected
>(and sometimes when you're preparing to eject them, depending on the
>system), which ought to make hotswap possible. I'm looking at
>integrating support for that into libata right now.
If the system supports S3/S4 hotswap, the corresponding ACPI resume
method
(_WAK in this case) will notify the hotswaped devices, so if the driver
can
handle the event, there is no problem for hotswap in suspend/resume.

Thanks,
Shaohua

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2005-12-07 23:19 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-12-06  6:10 [RFC]add ACPI hooks for IDE suspend/resume Shaohua Li
2005-12-06 22:20 ` Matthew Garrett
2005-12-06 22:50   ` Alan Cox
2005-12-07  8:17   ` Bartlomiej Zolnierkiewicz
2005-12-07 13:14     ` Matthew Garrett
2005-12-07 14:19       ` Bartlomiej Zolnierkiewicz
2005-12-07 14:26         ` Bartlomiej Zolnierkiewicz
2005-12-07 14:34           ` Matthew Garrett
2005-12-07 14:33         ` Matthew Garrett
2005-12-07 14:45           ` Bartlomiej Zolnierkiewicz
2005-12-07 14:58             ` Matthew Garrett
2005-12-07 15:44               ` Bartlomiej Zolnierkiewicz
2005-12-07 15:53                 ` Matthew Garrett
2005-12-07 15:41             ` Alan Cox
2005-12-07 15:49               ` Bartlomiej Zolnierkiewicz
2005-12-07  1:22                 ` Shaohua Li
2005-12-07 19:15                   ` Bartlomiej Zolnierkiewicz
2005-12-07 22:35                     ` Alan Cox
2005-12-07 22:42                       ` Bartlomiej Zolnierkiewicz
2005-12-07 22:43                       ` Pavel Machek
2005-12-07 22:48                         ` Matthew Garrett
2005-12-07  0:11 ` Randy.Dunlap
2005-12-07  1:15   ` Shaohua Li
  -- strict thread matches above, loose matches on Subject: below --
2005-12-07 21:58 Li, Shaohua
2005-12-07 23:19 Li, Shaohua

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox