On Tue, 2003-06-03 at 19:04, Benjamin Herrenschmidt wrote:
> On Tue, 2003-06-03 at 16:08, Alan Cox wrote:
> > Still races. Ben's stuff is needed
> I have it working with some fixes from what I sent earlier, I'll
> repost that tonight or tomorrow, I need to extract that from
> a half-broken tree ;)
Ok, here is it. It still need a bit of cleanup (removal of magic
number for "steps" -> enum, etc...) and we may want to do more
things on wakeup especially for ide-cd, Also I don't deal with
ide-tape or ide-floppy in there and haven't fully studied the
impact with ide-scsi....
So it's not meant to be merged as-is, though I'd appreciate to
know if it helps for you.
Bartolomiej: Any comments appreciated, I won't do the ide-tape/floppy
part as I don't know/own these, I think i'll let you decide if
anything more is needed on the wakeup path for ide-cd... Once I
have enough feedback, I'll send you a cleanified version as
candidate for upstream merge.
===== drivers/ide/ide-cd.c 1.49 vs edited =====
--- 1.49/drivers/ide/ide-cd.c Sun Jun 1 21:55:45 2003
+++ edited/drivers/ide/ide-cd.c Wed Jun 4 14:53:38 2003
@@ -3253,6 +3253,46 @@
static int ide_cdrom_attach (ide_drive_t *drive);
+static void ide_cdrom_complete_power_step (ide_drive_t *drive, ide_power_state_t *state, u8 stat, u8 error)
+{
+}
+
+/* Power Management state machine.
+ *
+ * We don't do much for CDs right now
+ */
+static ide_startstop_t ide_cdrom_start_power_step (ide_drive_t *drive, ide_power_state_t *state)
+{
+ ide_task_t args;
+
+ memset(&args, 0, sizeof(ide_task_t));
+
+ if (state->step == 0) {
+ state->step = state->suspend ? 1 : 101;
+ return ide_stopped;
+ }
+ switch(state->step) {
+ case 1:
+ break;
+
+ case 101: /* Resume step 1 (restore DMA) */
+ /* Right now, all we do is call ide_dma_check for the HWIF,
+ * we could be smarter and check for current xfer_speed
+ * in struct drive etc...
+ * Also, this step could be implemented as a generic helper
+ * as most subdrivers will use it
+ */
+ if (!drive->id || !(drive->id->capability & 1))
+ break;
+ if (HWIF(drive)->ide_dma_check == NULL)
+ break;
+ HWIF(drive)->ide_dma_check(drive);
+ break;
+ }
+ state->step = ide_power_state_completed;
+ return ide_stopped;
+}
+
static ide_driver_t ide_cdrom_driver = {
.owner = THIS_MODULE,
.name = "ide-cdrom",
@@ -3269,6 +3309,12 @@
.capacity = ide_cdrom_capacity,
.attach = ide_cdrom_attach,
.drives = LIST_HEAD_INIT(ide_cdrom_driver.drives),
+ .start_power_step = ide_cdrom_start_power_step,
+ .complete_power_step = ide_cdrom_complete_power_step,
+ .gen_driver = {
+ .suspend = generic_ide_suspend,
+ .resume = generic_ide_resume,
+ }
};
static int idecd_open(struct inode * inode, struct file * file)
===== drivers/ide/ide-disk.c 1.45 vs edited =====
--- 1.45/drivers/ide/ide-disk.c Sun Jun 1 21:55:48 2003
+++ edited/drivers/ide/ide-disk.c Wed Jun 4 14:54:24 2003
@@ -138,8 +138,6 @@
#ifndef CONFIG_IDE_TASKFILE_IO
-static int driver_blocked;
-
/*
* read_intr() is the handler for disk read/multread interrupts
*/
@@ -364,9 +362,6 @@
nsectors.all = (u16) rq->nr_sectors;
- if (driver_blocked)
- panic("Request while ide driver is blocked?");
-
if (drive->using_tcq && idedisk_start_tag(drive, rq)) {
if (!ata_pending_commands(drive))
BUG();
@@ -1392,21 +1387,6 @@
return 0;
}
-static int call_idedisk_standby (ide_drive_t *drive, int arg)
-{
- ide_task_t args;
- u8 standby = (arg) ? WIN_STANDBYNOW2 : WIN_STANDBYNOW1;
- memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_COMMAND_OFFSET] = standby;
- args.command_type = ide_cmd_type_parser(&args);
- return ide_raw_taskfile(drive, &args, NULL);
-}
-
-static int do_idedisk_standby (ide_drive_t *drive)
-{
- return call_idedisk_standby(drive, 0);
-}
-
static int do_idedisk_flushcache (ide_drive_t *drive)
{
ide_task_t args;
@@ -1505,37 +1485,66 @@
#endif
}
-static int idedisk_suspend(struct device *dev, u32 state, u32 level)
-{
- ide_drive_t *drive = dev->driver_data;
-
- printk("Suspending device %p\n", dev->driver_data);
-
- /* I hope that every freeze operation from the upper levels have
- * already been done...
- */
-
- if (level != SUSPEND_SAVE_STATE)
- return 0;
-
- /* set the drive to standby */
- printk(KERN_INFO "suspending: %s ", drive->name);
- do_idedisk_standby(drive);
- drive->blocked = 1;
- BUG_ON (HWGROUP(drive)->handler);
- return 0;
+static void idedisk_complete_power_step (ide_drive_t *drive, ide_power_state_t *state, u8 stat, u8 error)
+{
+ switch(state->step) {
+ case 1: /* Suspend step 1 (flush cache) complete */
+ state->step = 2;
+ break;
+ case 2: /* Suspend step 2 (standby) complete */
+ state->step = ide_power_state_completed;
+ break;
+ }
}
-static int idedisk_resume(struct device *dev, u32 level)
+/* Power Management state machine. This one is rather trivial for now,
+ * we should probably add more, like switching back to PIO on suspend
+ * to help some BIOSes, re-do the door locking on resume, etc...
+ */
+static ide_startstop_t idedisk_start_power_step (ide_drive_t *drive, ide_power_state_t *state)
{
- ide_drive_t *drive = dev->driver_data;
+ ide_task_t args;
- if (level != RESUME_RESTORE_STATE)
- return 0;
- BUG_ON(!drive->blocked);
- drive->blocked = 0;
- return 0;
+ memset(&args, 0, sizeof(ide_task_t));
+
+ if (state->step == 0) {
+ state->step = state->suspend ? 1 : 101;
+ return ide_stopped;
+ }
+ switch(state->step) {
+ case 1: /* Suspend step 1 (flush cache) */
+ if (!drive->wcache) {
+ idedisk_complete_power_step(drive, state, 0, 0);
+ return ide_stopped;
+ }
+ if (drive->id->cfs_enable_2 & 0x2400)
+ args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;
+ else
+ args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;
+ args.command_type = ide_cmd_type_parser(&args);
+ return do_rw_taskfile(drive, &args);
+ case 2: /* Suspend step 2 (standby) */
+ args.tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1;
+ args.command_type = ide_cmd_type_parser(&args);
+ return do_rw_taskfile(drive, &args);
+
+ case 101: /* Resume step 1 (restore DMA) */
+ /* Right now, all we do is call ide_dma_check for the HWIF,
+ * we could be smarter and check for current xfer_speed
+ * in struct drive etc...
+ * Also, this step could be implemented as a generic helper
+ * as most subdrivers will use it
+ */
+ if (!drive->id || !(drive->id->capability & 1))
+ break;
+ if (HWIF(drive)->ide_dma_check == NULL)
+ break;
+ HWIF(drive)->ide_dma_check(drive);
+ break;
+ }
+ state->step = ide_power_state_completed;
+ return ide_stopped;
}
static void idedisk_setup (ide_drive_t *drive)
@@ -1681,9 +1690,11 @@
.proc = idedisk_proc,
.attach = idedisk_attach,
.drives = LIST_HEAD_INIT(idedisk_driver.drives),
+ .start_power_step = idedisk_start_power_step,
+ .complete_power_step = idedisk_complete_power_step,
.gen_driver = {
- .suspend = idedisk_suspend,
- .resume = idedisk_resume,
+ .suspend = generic_ide_suspend,
+ .resume = generic_ide_resume,
}
};
===== drivers/ide/ide-io.c 1.11 vs edited =====
--- 1.11/drivers/ide/ide-io.c Mon May 12 02:09:46 2003
+++ edited/drivers/ide/ide-io.c Wed Jun 4 14:55:06 2003
@@ -139,6 +139,39 @@
EXPORT_SYMBOL(ide_end_request);
/**
+ * ide_complete_pm_request - end the current Power Management request
+ * @drive: target drive
+ * @rq: request
+ *
+ * This function cleans up the current PM request and stops the queue
+ * if necessary.
+ */
+
+static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
+{
+ ide_power_state_t *state = (ide_power_state_t *)rq->special;
+ unsigned long flags;
+ int suspend = state->suspend;
+
+#ifdef DEBUG_PM
+ printk("%s: completing PM request, suspend: %d\n", drive->name, state->suspend);
+#endif
+ spin_lock_irqsave(&ide_lock, flags);
+ if (suspend)
+ __blk_stop_queue(&drive->queue);
+ else
+ drive->blocked = 0;
+ blkdev_dequeue_request(rq);
+ HWGROUP(drive)->rq = NULL;
+ end_that_request_last(rq);
+ spin_unlock_irqrestore(&ide_lock, flags);
+ /* Hrm... there is no __blk_start_queue... */
+ if (!suspend)
+ blk_start_queue(&drive->queue);
+}
+
+
+/**
* ide_end_drive_cmd - end an explicit drive command
* @drive: command
* @stat: status bits
@@ -214,6 +247,17 @@
args->hobRegister[IDE_HCYL_OFFSET_HOB] = hwif->INB(IDE_HCYL_REG);
}
}
+ } else if (rq->flags & REQ_POWER_MANAGEMENT) {
+ ide_power_state_t *state = (ide_power_state_t *)rq->special;
+
+#ifdef DEBUG_PM
+ printk("%s: complete_power_step(susp: %d, step: %d, stat: %x, err: %x)\n",
+ drive->name, state->suspend, state->step, stat, err);
+#endif
+ DRIVER(drive)->complete_power_step(drive, state, stat, err);
+ if (state->step == ide_power_state_completed)
+ ide_complete_pm_request(drive, rq);
+ return;
}
spin_lock_irqsave(&ide_lock, flags);
@@ -615,7 +659,37 @@
while ((read_timer() - HWIF(drive)->last_time) < DISK_RECOVERY_TIME);
#endif
- SELECT_DRIVE(drive);
+ if (rq->flags & REQ_POWER_MANAGEMENT) {
+ ide_power_state_t *state = (ide_power_state_t *)rq->special;
+ int rc;
+
+ /* Mark drive blocked when starting the suspend sequence */
+ if (state->suspend && state->step == 0)
+ drive->blocked = 1;
+ /* If this is a power management wakeup request, the first thing
+ * we do is to wait for BSY bit to go away (with a looong timeout)
+ * as a drive on this hwif may just be POSTing itself. We do that
+ * before even selecting as the "other" device on the bus may be
+ * broken enough to walk on our toes at this point.
+ */
+ if (state->suspend != 0 || state->step != 0)
+ goto dont_wait;
+#ifdef DEBUG_PM
+ printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name);
+#endif
+ rc = ide_wait_not_busy(HWIF(drive), 35000);
+ if (rc)
+ printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
+ SELECT_DRIVE(drive);
+ HWIF(drive)->OUTB(8, HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]);
+ rc = ide_wait_not_busy(HWIF(drive),
...
read more »