[ide] generic Power Management for IDE devices

Move PM code from ide-cd.c and ide-disk.c to IDE core so:
* PM is supported for other ATAPI devices (floppy, tape)
* PM is supported even if specific driver is not loaded

Also s/HWIF(drive)/drive->hwif/ while at it.
Signed-off-by: default avatarBartlomiej Zolnierkiewicz <bzolnier@gmail.com>
parent 1ef372a6
...@@ -3251,45 +3251,6 @@ int ide_cdrom_cleanup(ide_drive_t *drive) ...@@ -3251,45 +3251,6 @@ int ide_cdrom_cleanup(ide_drive_t *drive)
static int ide_cdrom_attach (ide_drive_t *drive); static int ide_cdrom_attach (ide_drive_t *drive);
/*
* Power Management state machine.
*
* We don't do much for CDs right now.
*/
static void ide_cdrom_complete_power_step (ide_drive_t *drive, struct request *rq, u8 stat, u8 error)
{
}
static ide_startstop_t ide_cdrom_start_power_step (ide_drive_t *drive, struct request *rq)
{
ide_task_t *args = rq->special;
memset(args, 0, sizeof(*args));
switch (rq->pm->pm_step) {
case ide_pm_state_start_suspend:
break;
case ide_pm_state_start_resume: /* Resume step 1 (restore DMA) */
/*
* Right now, all we do is call hwif->ide_dma_check(drive),
* 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->capability & 1) == 0)
break;
if (HWIF(drive)->ide_dma_check == NULL)
break;
HWIF(drive)->ide_dma_check(drive);
break;
}
rq->pm->pm_step = ide_pm_state_completed;
return ide_stopped;
}
static ide_driver_t ide_cdrom_driver = { static ide_driver_t ide_cdrom_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "ide-cdrom", .name = "ide-cdrom",
...@@ -3302,8 +3263,6 @@ static ide_driver_t ide_cdrom_driver = { ...@@ -3302,8 +3263,6 @@ static ide_driver_t ide_cdrom_driver = {
.capacity = ide_cdrom_capacity, .capacity = ide_cdrom_capacity,
.attach = ide_cdrom_attach, .attach = ide_cdrom_attach,
.drives = LIST_HEAD_INIT(ide_cdrom_driver.drives), .drives = LIST_HEAD_INIT(ide_cdrom_driver.drives),
.start_power_step = ide_cdrom_start_power_step,
.complete_power_step = ide_cdrom_complete_power_step,
}; };
static int idecd_open(struct inode * inode, struct file * file) static int idecd_open(struct inode * inode, struct file * file)
......
...@@ -903,90 +903,6 @@ static void idedisk_add_settings(ide_drive_t *drive) ...@@ -903,90 +903,6 @@ static void idedisk_add_settings(ide_drive_t *drive)
ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL);
} }
/*
* 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...
*/
enum {
idedisk_pm_flush_cache = ide_pm_state_start_suspend,
idedisk_pm_standby,
idedisk_pm_idle = ide_pm_state_start_resume,
idedisk_pm_restore_dma,
};
static void idedisk_complete_power_step (ide_drive_t *drive, struct request *rq, u8 stat, u8 error)
{
switch (rq->pm->pm_step) {
case idedisk_pm_flush_cache: /* Suspend step 1 (flush cache) complete */
if (rq->pm->pm_state == 4)
rq->pm->pm_step = ide_pm_state_completed;
else
rq->pm->pm_step = idedisk_pm_standby;
break;
case idedisk_pm_standby: /* Suspend step 2 (standby) complete */
rq->pm->pm_step = ide_pm_state_completed;
break;
case idedisk_pm_idle: /* Resume step 1 (idle) complete */
rq->pm->pm_step = idedisk_pm_restore_dma;
break;
}
}
static ide_startstop_t idedisk_start_power_step (ide_drive_t *drive, struct request *rq)
{
ide_task_t *args = rq->special;
memset(args, 0, sizeof(*args));
switch (rq->pm->pm_step) {
case idedisk_pm_flush_cache: /* Suspend step 1 (flush cache) */
/* Not supported? Switch to next step now. */
if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) {
idedisk_complete_power_step(drive, rq, 0, 0);
return ide_stopped;
}
if (ide_id_has_flush_cache_ext(drive->id))
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;
else
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = &task_no_data_intr;
return do_rw_taskfile(drive, args);
case idedisk_pm_standby: /* Suspend step 2 (standby) */
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = &task_no_data_intr;
return do_rw_taskfile(drive, args);
case idedisk_pm_idle: /* Resume step 1 (idle) */
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = task_no_data_intr;
return do_rw_taskfile(drive, args);
case idedisk_pm_restore_dma: /* Resume step 2 (restore DMA) */
/*
* Right now, all we do is call hwif->ide_dma_check(drive),
* 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->capability & 1) == 0)
break;
if (HWIF(drive)->ide_dma_check == NULL)
break;
HWIF(drive)->ide_dma_check(drive);
break;
}
rq->pm->pm_step = ide_pm_state_completed;
return ide_stopped;
}
static void idedisk_setup (ide_drive_t *drive) static void idedisk_setup (ide_drive_t *drive)
{ {
struct hd_driveid *id = drive->id; struct hd_driveid *id = drive->id;
...@@ -1231,8 +1147,6 @@ static ide_driver_t idedisk_driver = { ...@@ -1231,8 +1147,6 @@ static ide_driver_t idedisk_driver = {
.proc = idedisk_proc, .proc = idedisk_proc,
.attach = idedisk_attach, .attach = idedisk_attach,
.drives = LIST_HEAD_INIT(idedisk_driver.drives), .drives = LIST_HEAD_INIT(idedisk_driver.drives),
.start_power_step = idedisk_start_power_step,
.complete_power_step = idedisk_complete_power_step,
}; };
static int idedisk_open(struct inode *inode, struct file *filp) static int idedisk_open(struct inode *inode, struct file *filp)
......
...@@ -129,6 +129,99 @@ int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors) ...@@ -129,6 +129,99 @@ int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
} }
EXPORT_SYMBOL(ide_end_request); EXPORT_SYMBOL(ide_end_request);
/*
* 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...
*/
enum {
ide_pm_flush_cache = ide_pm_state_start_suspend,
idedisk_pm_standby,
idedisk_pm_idle = ide_pm_state_start_resume,
ide_pm_restore_dma,
};
static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error)
{
if (drive->media != ide_disk)
return;
switch (rq->pm->pm_step) {
case ide_pm_flush_cache: /* Suspend step 1 (flush cache) complete */
if (rq->pm->pm_state == 4)
rq->pm->pm_step = ide_pm_state_completed;
else
rq->pm->pm_step = idedisk_pm_standby;
break;
case idedisk_pm_standby: /* Suspend step 2 (standby) complete */
rq->pm->pm_step = ide_pm_state_completed;
break;
case idedisk_pm_idle: /* Resume step 1 (idle) complete */
rq->pm->pm_step = ide_pm_restore_dma;
break;
}
}
static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
{
ide_task_t *args = rq->special;
memset(args, 0, sizeof(*args));
if (drive->media != ide_disk) {
/* skip idedisk_pm_idle for ATAPI devices */
if (rq->pm->pm_step == idedisk_pm_idle)
rq->pm->pm_step = ide_pm_restore_dma;
}
switch (rq->pm->pm_step) {
case ide_pm_flush_cache: /* Suspend step 1 (flush cache) */
if (drive->media != ide_disk)
break;
/* Not supported? Switch to next step now. */
if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) {
ide_complete_power_step(drive, rq, 0, 0);
return ide_stopped;
}
if (ide_id_has_flush_cache_ext(drive->id))
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;
else
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = &task_no_data_intr;
return do_rw_taskfile(drive, args);
case idedisk_pm_standby: /* Suspend step 2 (standby) */
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = &task_no_data_intr;
return do_rw_taskfile(drive, args);
case idedisk_pm_idle: /* Resume step 1 (idle) */
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = task_no_data_intr;
return do_rw_taskfile(drive, args);
case ide_pm_restore_dma: /* Resume step 2 (restore DMA) */
/*
* Right now, all we do is call hwif->ide_dma_check(drive),
* we could be smarter and check for current xfer_speed
* in struct drive etc...
*/
if ((drive->id->capability & 1) == 0)
break;
if (drive->hwif->ide_dma_check == NULL)
break;
drive->hwif->ide_dma_check(drive);
break;
}
rq->pm->pm_step = ide_pm_state_completed;
return ide_stopped;
}
/** /**
* ide_complete_pm_request - end the current Power Management request * ide_complete_pm_request - end the current Power Management request
* @drive: target drive * @drive: target drive
...@@ -276,7 +369,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) ...@@ -276,7 +369,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n", printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",
drive->name, rq->pm->pm_step, stat, err); drive->name, rq->pm->pm_step, stat, err);
#endif #endif
DRIVER(drive)->complete_power_step(drive, rq, stat, err); ide_complete_power_step(drive, rq, stat, err);
if (rq->pm->pm_step == ide_pm_state_completed) if (rq->pm->pm_step == ide_pm_state_completed)
ide_complete_pm_request(drive, rq); ide_complete_pm_request(drive, rq);
return; return;
...@@ -770,7 +863,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) ...@@ -770,7 +863,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
printk("%s: start_power_step(step: %d)\n", printk("%s: start_power_step(step: %d)\n",
drive->name, rq->pm->pm_step); drive->name, rq->pm->pm_step);
#endif #endif
startstop = DRIVER(drive)->start_power_step(drive, rq); startstop = ide_start_power_step(drive, rq);
if (startstop == ide_stopped && if (startstop == ide_stopped &&
rq->pm->pm_step == ide_pm_state_completed) rq->pm->pm_step == ide_pm_state_completed)
ide_complete_pm_request(drive, rq); ide_complete_pm_request(drive, rq);
......
...@@ -2065,13 +2065,6 @@ static ide_startstop_t default_abort(ide_drive_t *drive, struct request *rq) ...@@ -2065,13 +2065,6 @@ static ide_startstop_t default_abort(ide_drive_t *drive, struct request *rq)
return __ide_abort(drive, rq); return __ide_abort(drive, rq);
} }
static ide_startstop_t default_start_power_step(ide_drive_t *drive,
struct request *rq)
{
rq->pm->pm_step = ide_pm_state_completed;
return ide_stopped;
}
static void setup_driver_defaults (ide_driver_t *d) static void setup_driver_defaults (ide_driver_t *d)
{ {
BUG_ON(d->attach == NULL || d->cleanup == NULL); BUG_ON(d->attach == NULL || d->cleanup == NULL);
...@@ -2083,8 +2076,6 @@ static void setup_driver_defaults (ide_driver_t *d) ...@@ -2083,8 +2076,6 @@ static void setup_driver_defaults (ide_driver_t *d)
if (d->pre_reset == NULL) d->pre_reset = default_pre_reset; if (d->pre_reset == NULL) d->pre_reset = default_pre_reset;
if (d->capacity == NULL) d->capacity = default_capacity; if (d->capacity == NULL) d->capacity = default_capacity;
if (d->special == NULL) d->special = default_special; if (d->special == NULL) d->special = default_special;
if (d->start_power_step == NULL)
d->start_power_step = default_start_power_step;
} }
int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver) int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver)
......
...@@ -1104,8 +1104,6 @@ typedef struct ide_driver_s { ...@@ -1104,8 +1104,6 @@ typedef struct ide_driver_s {
int (*attach)(ide_drive_t *); int (*attach)(ide_drive_t *);
void (*ata_prebuilder)(ide_drive_t *); void (*ata_prebuilder)(ide_drive_t *);
void (*atapi_prebuilder)(ide_drive_t *); void (*atapi_prebuilder)(ide_drive_t *);
ide_startstop_t (*start_power_step)(ide_drive_t *, struct request *);
void (*complete_power_step)(ide_drive_t *, struct request *, u8, u8);
struct device_driver gen_driver; struct device_driver gen_driver;
struct list_head drives; struct list_head drives;
struct list_head drivers; struct list_head drivers;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment