#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/kmod.h>
+#include <linux/scatterlist.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/bitops.h>
-/**
- * ide_end_request - complete an IDE I/O
- * @drive: IDE device for the I/O
- * @uptodate:
- * @nr_sectors: number of sectors completed
- *
- * This is our end_request wrapper function. We complete the I/O
- * update random number input and dequeue the request, which if
- * it was tagged may be out of order.
- */
-
-int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
+static int __ide_end_request(ide_drive_t *drive, struct request *rq,
+ int uptodate, int nr_sectors)
{
- struct request *rq;
- unsigned long flags;
int ret = 1;
- spin_lock_irqsave(&ide_lock, flags);
- rq = HWGROUP(drive)->rq;
-
BUG_ON(!(rq->flags & REQ_STARTED));
- if (!nr_sectors)
- nr_sectors = rq->hard_cur_sectors;
-
/*
* if failfast is set on a request, override number of sectors and
* complete the whole request right now
*/
- if (blk_noretry_request(rq) && !uptodate)
+ if (blk_noretry_request(rq) && end_io_error(uptodate))
nr_sectors = rq->hard_nr_sectors;
+ if (!blk_fs_request(rq) && end_io_error(uptodate) && !rq->errors)
+ rq->errors = -EIO;
+
/*
* decide whether to reenable DMA -- 3 is a random magic for now,
* if we DMA timeout more than 3 times, just stay in PIO
if (!end_that_request_first(rq, uptodate, nr_sectors)) {
add_disk_randomness(rq->rq_disk);
- if (!blk_rq_tagged(rq))
- blkdev_dequeue_request(rq);
- else
- blk_queue_end_tag(drive->queue, rq);
+ blkdev_dequeue_request(rq);
HWGROUP(drive)->rq = NULL;
- end_that_request_last(rq);
+ end_that_request_last(rq, uptodate);
ret = 0;
}
- spin_unlock_irqrestore(&ide_lock, flags);
+
return ret;
}
+/**
+ * ide_end_request - complete an IDE I/O
+ * @drive: IDE device for the I/O
+ * @uptodate:
+ * @nr_sectors: number of sectors completed
+ *
+ * This is our end_request wrapper function. We complete the I/O
+ * update random number input and dequeue the request, which if
+ * it was tagged may be out of order.
+ */
+
+int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
+{
+ struct request *rq;
+ unsigned long flags;
+ int ret = 1;
+
+ /*
+ * room for locking improvements here, the calls below don't
+ * need the queue lock held at all
+ */
+ spin_lock_irqsave(&ide_lock, flags);
+ rq = HWGROUP(drive)->rq;
+
+ if (!nr_sectors)
+ nr_sectors = rq->hard_cur_sectors;
+
+ ret = __ide_end_request(drive, rq, uptodate, nr_sectors);
+
+ spin_unlock_irqrestore(&ide_lock, flags);
+ return ret;
+}
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 == PM_EVENT_FREEZE)
+ 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
* @drive: target drive
}
blkdev_dequeue_request(rq);
HWGROUP(drive)->rq = NULL;
- end_that_request_last(rq);
+ end_that_request_last(rq, 1);
spin_unlock_irqrestore(&ide_lock, flags);
}
+/*
+ * FIXME: probably move this somewhere else, name is bad too :)
+ */
+u64 ide_get_error_location(ide_drive_t *drive, char *args)
+{
+ u32 high, low;
+ u8 hcyl, lcyl, sect;
+ u64 sector;
+
+ high = 0;
+ hcyl = args[5];
+ lcyl = args[4];
+ sect = args[3];
+
+ if (ide_id_has_flush_cache_ext(drive->id)) {
+ low = (hcyl << 16) | (lcyl << 8) | sect;
+ HWIF(drive)->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);
+ high = ide_read_24(drive);
+ } else {
+ u8 cur = HWIF(drive)->INB(IDE_SELECT_REG);
+ if (cur & 0x40) {
+ high = cur & 0xf;
+ low = (hcyl << 16) | (lcyl << 8) | sect;
+ } else {
+ low = hcyl * drive->head * drive->sect;
+ low += lcyl * drive->sect;
+ low += sect - 1;
+ }
+ }
+
+ sector = ((u64) high << 24) | low;
+ return sector;
+}
+EXPORT_SYMBOL(ide_get_error_location);
+
/**
* ide_end_drive_cmd - end an explicit drive command
* @drive: command
args->hobRegister[IDE_DATA_OFFSET] = (data >> 8) & 0xFF;
}
args->tfRegister[IDE_ERROR_OFFSET] = err;
+ /* be sure we're looking at the low order bits */
+ hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);
args->tfRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG);
args->tfRegister[IDE_SECTOR_OFFSET] = hwif->INB(IDE_SECTOR_REG);
args->tfRegister[IDE_LCYL_OFFSET] = hwif->INB(IDE_LCYL_REG);
printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",
drive->name, rq->pm->pm_step, stat, err);
#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)
ide_complete_pm_request(drive, rq);
return;
spin_lock_irqsave(&ide_lock, flags);
blkdev_dequeue_request(rq);
HWGROUP(drive)->rq = NULL;
- end_that_request_last(rq);
+ rq->errors = err;
+ end_that_request_last(rq, !rq->errors);
spin_unlock_irqrestore(&ide_lock, flags);
}
* by read a sector's worth of data from the drive. Of course,
* this may not help if the drive is *waiting* for data from *us*.
*/
-void try_to_flush_leftover_data (ide_drive_t *drive)
+static void try_to_flush_leftover_data (ide_drive_t *drive)
{
int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
}
}
-EXPORT_SYMBOL(try_to_flush_leftover_data);
+static void ide_kill_rq(ide_drive_t *drive, struct request *rq)
+{
+ if (rq->rq_disk) {
+ ide_driver_t *drv;
-/*
- * FIXME Add an ATAPI error
- */
+ drv = *(ide_driver_t **)rq->rq_disk->private_data;
+ drv->end_request(drive, 0, 0);
+ } else
+ ide_end_request(drive, 0, 0);
+}
+
+static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
+ /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else if (stat & ERR_STAT) {
+ /* err has different meaning on cdrom and tape */
+ if (err == ABRT_ERR) {
+ if (drive->select.b.lba &&
+ /* some newer drives don't support WIN_SPECIFY */
+ hwif->INB(IDE_COMMAND_REG) == WIN_SPECIFY)
+ return ide_stopped;
+ } else if ((err & BAD_CRC) == BAD_CRC) {
+ /* UDMA crc error, just retry the operation */
+ drive->crc_count++;
+ } else if (err & (BBD_ERR | ECC_ERR)) {
+ /* retries won't help these */
+ rq->errors = ERROR_MAX;
+ } else if (err & TRK0_ERR) {
+ /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ }
+
+ if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ)
+ try_to_flush_leftover_data(drive);
+
+ if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))
+ /* force an abort */
+ hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG);
+
+ if (rq->errors >= ERROR_MAX || blk_noretry_request(rq))
+ ide_kill_rq(drive, rq);
+ else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ return ide_do_reset(drive);
+ }
+ if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
+ ++rq->errors;
+ }
+ return ide_stopped;
+}
+
+static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
+ /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ /* add decoding error stuff */
+ }
+
+ if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))
+ /* force an abort */
+ hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG);
+
+ if (rq->errors >= ERROR_MAX) {
+ ide_kill_rq(drive, rq);
+ } else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ return ide_do_reset(drive);
+ }
+ ++rq->errors;
+ }
+
+ return ide_stopped;
+}
+
+ide_startstop_t
+__ide_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
+{
+ if (drive->media == ide_disk)
+ return ide_ata_error(drive, rq, stat, err);
+ return ide_atapi_error(drive, rq, stat, err);
+}
+
+EXPORT_SYMBOL_GPL(__ide_error);
/**
* ide_error - handle an error on the IDE
ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat)
{
- ide_hwif_t *hwif;
struct request *rq;
u8 err;
err = ide_dump_status(drive, msg, stat);
- if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
+
+ if ((rq = HWGROUP(drive)->rq) == NULL)
return ide_stopped;
- hwif = HWIF(drive);
/* retry only "normal" I/O: */
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) {
- rq->errors = 1;
- ide_end_drive_cmd(drive, stat, err);
- return ide_stopped;
- }
- if (rq->flags & REQ_DRIVE_TASKFILE) {
+ if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {
rq->errors = 1;
ide_end_drive_cmd(drive, stat, err);
return ide_stopped;
}
- if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
- /* other bits are useless when BUSY */
+ if (rq->rq_disk) {
+ ide_driver_t *drv;
+
+ drv = *(ide_driver_t **)rq->rq_disk->private_data;
+ return drv->error(drive, rq, stat, err);
+ } else
+ return __ide_error(drive, rq, stat, err);
+}
+
+EXPORT_SYMBOL_GPL(ide_error);
+
+ide_startstop_t __ide_abort(ide_drive_t *drive, struct request *rq)
+{
+ if (drive->media != ide_disk)
rq->errors |= ERROR_RESET;
- } else {
- if (drive->media != ide_disk)
- goto media_out;
-
- if (stat & ERR_STAT) {
- /* err has different meaning on cdrom and tape */
- if (err == ABRT_ERR) {
- if (drive->select.b.lba &&
- (hwif->INB(IDE_COMMAND_REG) == WIN_SPECIFY))
- /* some newer drives don't
- * support WIN_SPECIFY
- */
- return ide_stopped;
- } else if ((err & BAD_CRC) == BAD_CRC) {
- drive->crc_count++;
- /* UDMA crc error -- just retry the operation */
- } else if (err & (BBD_ERR | ECC_ERR)) {
- /* retries won't help these */
- rq->errors = ERROR_MAX;
- } else if (err & TRK0_ERR) {
- /* help it find track zero */
- rq->errors |= ERROR_RECAL;
- }
- }
-media_out:
- if ((stat & DRQ_STAT) && rq_data_dir(rq) != WRITE)
- try_to_flush_leftover_data(drive);
- }
- if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) {
- /* force an abort */
- hwif->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);
- }
- if (rq->errors >= ERROR_MAX) {
- DRIVER(drive)->end_request(drive, 0, 0);
- } else {
- if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
- ++rq->errors;
- return ide_do_reset(drive);
- }
- if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
- drive->special.b.recalibrate = 1;
- ++rq->errors;
- }
+
+ ide_kill_rq(drive, rq);
+
return ide_stopped;
}
-EXPORT_SYMBOL(ide_error);
+EXPORT_SYMBOL_GPL(__ide_abort);
/**
- * ide_abort - abort pending IDE operatins
+ * ide_abort - abort pending IDE operations
* @drive: drive the error occurred on
* @msg: message to report
*
ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg)
{
- ide_hwif_t *hwif;
struct request *rq;
if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
return ide_stopped;
- hwif = HWIF(drive);
/* retry only "normal" I/O: */
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) {
- rq->errors = 1;
- ide_end_drive_cmd(drive, BUSY_STAT, 0);
- return ide_stopped;
- }
- if (rq->flags & REQ_DRIVE_TASKFILE) {
+ if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {
rq->errors = 1;
ide_end_drive_cmd(drive, BUSY_STAT, 0);
return ide_stopped;
}
- rq->errors |= ERROR_RESET;
- DRIVER(drive)->end_request(drive, 0, 0);
- return ide_stopped;
-}
+ if (rq->rq_disk) {
+ ide_driver_t *drv;
-EXPORT_SYMBOL(ide_abort);
+ drv = *(ide_driver_t **)rq->rq_disk->private_data;
+ return drv->abort(drive, rq);
+ } else
+ return __ide_abort(drive, rq);
+}
/**
* ide_cmd - issue a simple drive command
* The drive must be selected beforehand.
*/
-void ide_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, ide_handler_t *handler)
+static void ide_cmd (ide_drive_t *drive, u8 cmd, u8 nsect,
+ ide_handler_t *handler)
{
ide_hwif_t *hwif = HWIF(drive);
if (IDE_CONTROL_REG)
ide_execute_command(drive, cmd, handler, WAIT_CMD, NULL);
}
-EXPORT_SYMBOL(ide_cmd);
-
/**
* drive_cmd_intr - drive command completion interrupt
* @drive: drive the completion interrupt occurred on
*
* drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
- * We do any necessary daya reading and then wait for the drive to
+ * We do any necessary data reading and then wait for the drive to
* go non busy. At that point we may read the error data and complete
* the request
*/
-ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
+static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
{
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
udelay(100);
}
- if (!OK_STAT(stat, READY_STAT, BAD_STAT) && DRIVER(drive) != NULL)
- return DRIVER(drive)->error(drive, "drive_cmd", stat);
+ if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+ return ide_error(drive, "drive_cmd", stat);
/* calls ide_end_drive_cmd */
ide_end_drive_cmd(drive, stat, hwif->INB(IDE_ERROR_REG));
return ide_stopped;
}
-EXPORT_SYMBOL(drive_cmd_intr);
+static void ide_init_specify_cmd(ide_drive_t *drive, ide_task_t *task)
+{
+ task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect;
+ task->tfRegister[IDE_SECTOR_OFFSET] = drive->sect;
+ task->tfRegister[IDE_LCYL_OFFSET] = drive->cyl;
+ task->tfRegister[IDE_HCYL_OFFSET] = drive->cyl>>8;
+ task->tfRegister[IDE_SELECT_OFFSET] = ((drive->head-1)|drive->select.all)&0xBF;
+ task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SPECIFY;
+
+ task->handler = &set_geometry_intr;
+}
+
+static void ide_init_restore_cmd(ide_drive_t *drive, ide_task_t *task)
+{
+ task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect;
+ task->tfRegister[IDE_COMMAND_OFFSET] = WIN_RESTORE;
+
+ task->handler = &recal_intr;
+}
+
+static void ide_init_setmult_cmd(ide_drive_t *drive, ide_task_t *task)
+{
+ task->tfRegister[IDE_NSECTOR_OFFSET] = drive->mult_req;
+ task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETMULT;
+
+ task->handler = &set_multmode_intr;
+}
+
+static ide_startstop_t ide_disk_special(ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+ ide_task_t args;
+
+ memset(&args, 0, sizeof(ide_task_t));
+ args.command_type = IDE_DRIVE_TASK_NO_DATA;
+
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ ide_init_specify_cmd(drive, &args);
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ ide_init_restore_cmd(drive, &args);
+ } else if (s->b.set_multmode) {
+ s->b.set_multmode = 0;
+ if (drive->mult_req > drive->id->max_multsect)
+ drive->mult_req = drive->id->max_multsect;
+ ide_init_setmult_cmd(drive, &args);
+ } else if (s->all) {
+ int special = s->all;
+ s->all = 0;
+ printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+ return ide_stopped;
+ }
+
+ do_rw_taskfile(drive, &args);
+
+ return ide_started;
+}
/**
* do_special - issue some special commands
* back.
*/
-ide_startstop_t do_special (ide_drive_t *drive)
+static ide_startstop_t do_special (ide_drive_t *drive)
{
special_t *s = &drive->special;
if (HWIF(drive)->tuneproc != NULL)
HWIF(drive)->tuneproc(drive, drive->tune_req);
return ide_stopped;
+ } else {
+ if (drive->media == ide_disk)
+ return ide_disk_special(drive);
+
+ s->all = 0;
+ drive->mult_req = 0;
+ return ide_stopped;
+ }
+}
+
+void ide_map_sg(ide_drive_t *drive, struct request *rq)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct scatterlist *sg = hwif->sg_table;
+
+ if (hwif->sg_mapped) /* needed by ide-scsi */
+ return;
+
+ if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) {
+ hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg);
+ } else {
+ sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE);
+ hwif->sg_nents = 1;
}
- else
- return DRIVER(drive)->special(drive);
}
-EXPORT_SYMBOL(do_special);
+EXPORT_SYMBOL_GPL(ide_map_sg);
+
+void ide_init_sg_cmd(ide_drive_t *drive, struct request *rq)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ hwif->nsect = hwif->nleft = rq->nr_sectors;
+ hwif->cursg = hwif->cursg_ofs = 0;
+}
+
+EXPORT_SYMBOL_GPL(ide_init_sg_cmd);
/**
* execute_drive_command - issue special drive command
- * @drive: the drive to issue th command on
+ * @drive: the drive to issue the command on
* @rq: the request structure holding the command
*
* execute_drive_cmd() issues a special drive command, usually
* all commands to finish. Don't do this as that is due to change
*/
-ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
+ struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
if (rq->flags & REQ_DRIVE_TASKFILE) {
if (!args)
goto done;
-
+
+ hwif->data_phase = args->data_phase;
+
+ switch (hwif->data_phase) {
+ case TASKFILE_MULTI_OUT:
+ case TASKFILE_OUT:
+ case TASKFILE_MULTI_IN:
+ case TASKFILE_IN:
+ ide_init_sg_cmd(drive, rq);
+ ide_map_sg(drive, rq);
+ default:
+ break;
+ }
+
if (args->tf_out_flags.all != 0)
return flagged_taskfile(drive, args);
return do_rw_taskfile(drive, args);
return ide_stopped;
}
-EXPORT_SYMBOL(execute_drive_cmd);
-
/**
* start_request - start of I/O and command issuing for IDE
*
* FIXME: this function needs a rename
*/
-ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
+static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
sector_t block;
goto kill_rq;
}
- /*
- * bail early if we've sent a device to sleep, however how to wake
- * this needs to be a masked flag. FIXME for proper operations.
- */
- if (drive->suspend_reset)
- goto kill_rq;
-
block = rq->sector;
if (blk_fs_request(rq) &&
(drive->media == ide_disk || drive->media == ide_floppy)) {
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), 10000);
+ rc = ide_wait_not_busy(HWIF(drive), 100000);
if (rc)
printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
}
return startstop;
}
if (!drive->special.all) {
+ ide_driver_t *drv;
+
if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK))
return execute_drive_cmd(drive, rq);
else if (rq->flags & REQ_DRIVE_TASKFILE)
printk("%s: start_power_step(step: %d)\n",
drive->name, rq->pm->pm_step);
#endif
- startstop = DRIVER(drive)->start_power_step(drive, rq);
+ startstop = ide_start_power_step(drive, rq);
if (startstop == ide_stopped &&
rq->pm->pm_step == ide_pm_state_completed)
ide_complete_pm_request(drive, rq);
return startstop;
}
- return (DRIVER(drive)->do_request(drive, rq, block));
+
+ drv = *(ide_driver_t **)rq->rq_disk->private_data;
+ return drv->do_request(drive, rq, block);
}
return do_special(drive);
kill_rq:
- DRIVER(drive)->end_request(drive, 0, 0);
+ ide_kill_rq(drive, rq);
return ide_stopped;
}
-EXPORT_SYMBOL(start_request);
-
/**
* ide_stall_queue - pause an IDE device
* @drive: drive to stall
if (timeout > WAIT_WORSTCASE)
timeout = WAIT_WORSTCASE;
drive->sleep = timeout + jiffies;
+ drive->sleeping = 1;
}
EXPORT_SYMBOL(ide_stall_queue);
repeat:
best = NULL;
drive = hwgroup->drive;
+
+ /*
+ * drive is doing pre-flush, ordered write, post-flush sequence. even
+ * though that is 3 requests, it must be seen as a single transaction.
+ * we must not preempt this drive until that is complete
+ */
+ if (blk_queue_flushing(drive->queue)) {
+ /*
+ * small race where queue could get replugged during
+ * the 3-request flush cycle, just yank the plug since
+ * we want it to finish asap
+ */
+ blk_remove_plug(drive->queue);
+ return drive;
+ }
+
do {
- if ((!drive->sleep || time_after_eq(jiffies, drive->sleep))
+ if ((!drive->sleeping || time_after_eq(jiffies, drive->sleep))
&& !elv_queue_empty(drive->queue)) {
if (!best
- || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
- || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
+ || (drive->sleeping && (!best->sleeping || time_before(drive->sleep, best->sleep)))
+ || (!best->sleeping && time_before(WAKEUP(drive), WAKEUP(best))))
{
if (!blk_queue_plugged(drive->queue))
best = drive;
}
}
} while ((drive = drive->next) != hwgroup->drive);
- if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
+ if (best && best->nice1 && !best->sleeping && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
long t = (signed long)(WAKEUP(best) - jiffies);
if (t >= WAIT_MIN_SLEEP) {
/*
*/
drive = best->next;
do {
- if (!drive->sleep
- /* FIXME: use time_before */
- && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time))
- && 0 < (signed long)((jiffies + t) - WAKEUP(drive)))
+ if (!drive->sleeping
+ && time_before(jiffies - best->service_time, WAKEUP(drive))
+ && time_before(WAKEUP(drive), jiffies + t))
{
ide_stall_queue(best, min_t(long, t, 10 * WAIT_MIN_SLEEP));
goto repeat;
* the driver. This makes the driver much more friendlier to shared IRQs
* than previous designs, while remaining 100% (?) SMP safe and capable.
*/
-/* --BenH: made non-static as ide-pmac.c uses it to kick the hwgroup back
- * into life on wakeup from machine sleep.
- */
-void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
+static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
{
ide_drive_t *drive;
ide_hwif_t *hwif;
struct request *rq;
ide_startstop_t startstop;
+ int loops = 0;
/* for atari only: POSSIBLY BROKEN HERE(?) */
ide_get_lock(ide_intr, hwgroup);
hwgroup->busy = 1;
drive = choose_drive(hwgroup);
if (drive == NULL) {
- unsigned long sleep = 0;
+ int sleeping = 0;
+ unsigned long sleep = 0; /* shut up, gcc */
hwgroup->rq = NULL;
drive = hwgroup->drive;
do {
- if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
+ if (drive->sleeping && (!sleeping || time_before(drive->sleep, sleep))) {
+ sleeping = 1;
sleep = drive->sleep;
+ }
} while ((drive = drive->next) != hwgroup->drive);
- if (sleep) {
+ if (sleeping) {
/*
* Take a short snooze, and then wake up this hwgroup again.
* This gives other hwgroups on the same a chance to
/* no more work for this hwgroup (for now) */
return;
}
+ again:
hwif = HWIF(drive);
if (hwgroup->hwif->sharing_irq &&
hwif != hwgroup->hwif &&
}
hwgroup->hwif = hwif;
hwgroup->drive = drive;
- drive->sleep = 0;
+ drive->sleeping = 0;
drive->service_start = jiffies;
-queue_next:
- if (!ata_can_queue(drive)) {
- if (!ata_pending_commands(drive))
- hwgroup->busy = 0;
-
- break;
- }
-
if (blk_queue_plugged(drive->queue)) {
- if (drive->using_tcq)
- break;
-
printk(KERN_ERR "ide: huh? queue was plugged!\n");
break;
}
*/
rq = elv_next_request(drive->queue);
if (!rq) {
- hwgroup->busy = !!ata_pending_commands(drive);
+ hwgroup->busy = 0;
break;
}
* though. I hope that doesn't happen too much, hopefully not
* unless the subdriver triggers such a thing in its own PM
* state machine.
+ *
+ * We count how many times we loop here to make sure we service
+ * all drives in the hwgroup without looping for ever
*/
if (drive->blocked && !blk_pm_request(rq) && !(rq->flags & REQ_PREEMPT)) {
+ drive = drive->next ? drive->next : hwgroup->drive;
+ if (loops++ < 4 && !blk_queue_plugged(drive->queue))
+ goto again;
/* We clear busy, there should be no pending ATA command at this point. */
hwgroup->busy = 0;
break;
}
- if (!rq->bio && ata_pending_commands(drive))
- break;
-
hwgroup->rq = rq;
/*
* happens anyway when any interrupt comes in, IDE or otherwise
* -- the kernel masks the IRQ while it is being handled.
*/
- if (hwif->irq != masked_irq)
+ if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
disable_irq_nosync(hwif->irq);
spin_unlock(&ide_lock);
local_irq_enable();
/* allow other IRQs while we start this request */
startstop = start_request(drive, rq);
spin_lock_irq(&ide_lock);
- if (hwif->irq != masked_irq)
+ if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
enable_irq(hwif->irq);
- if (startstop == ide_released)
- goto queue_next;
if (startstop == ide_stopped)
hwgroup->busy = 0;
}
}
-EXPORT_SYMBOL(ide_do_request);
-
/*
* Passes the stuff to ide_do_request
*/
void do_ide_request(request_queue_t *q)
{
- ide_do_request(q->queuedata, IDE_NO_IRQ);
+ ide_drive_t *drive = q->queuedata;
+
+ ide_do_request(HWGROUP(drive), IDE_NO_IRQ);
}
/*
if (error < 0) {
printk(KERN_WARNING "%s: DMA timeout error\n", drive->name);
(void)HWIF(drive)->ide_dma_end(drive);
- ret = DRIVER(drive)->error(drive, "dma timeout error",
+ ret = ide_error(drive, "dma timeout error",
hwif->INB(IDE_STATUS_REG));
} else {
printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name);
HWGROUP(drive)->rq = NULL;
rq->errors = 0;
+
+ if (!rq->bio)
+ goto out;
+
rq->sector = rq->bio->bi_sector;
rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
rq->hard_cur_sectors = rq->current_nr_sectors;
- if (rq->bio)
- rq->buffer = NULL;
-
+ rq->buffer = bio_data(rq->bio);
+out:
return ret;
}
/* local CPU only,
* as if we were handling an interrupt */
local_irq_disable();
- if (hwgroup->poll_timeout != 0) {
+ if (hwgroup->polling) {
startstop = handler(drive);
} else if (drive_is_ready(drive)) {
if (drive->waiting_for_dma)
startstop = ide_dma_timeout_retry(drive, wait);
} else
startstop =
- DRIVER(drive)->error(drive, "irq timeout", hwif->INB(IDE_STATUS_REG));
+ ide_error(drive, "irq timeout", hwif->INB(IDE_STATUS_REG));
}
drive->service_time = jiffies - drive->service_start;
spin_lock_irq(&ide_lock);
spin_unlock_irqrestore(&ide_lock, flags);
}
-EXPORT_SYMBOL(ide_timer_expiry);
-
/**
* unexpected_intr - handle an unexpected IDE interrupt
* @irq: interrupt line
return IRQ_NONE;
}
- if ((handler = hwgroup->handler) == NULL ||
- hwgroup->poll_timeout != 0) {
+ if ((handler = hwgroup->handler) == NULL || hwgroup->polling) {
/*
* Not expecting an interrupt from this drive.
* That means this could be:
return IRQ_HANDLED;
}
-EXPORT_SYMBOL(ide_intr);
-
/**
* ide_init_drive_cmd - initialize a drive command request
* @rq: request object
{
memset(rq, 0, sizeof(*rq));
rq->flags = REQ_DRIVE_CMD;
+ rq->ref_count = 1;
}
EXPORT_SYMBOL(ide_init_drive_cmd);
* for the new rq to be completed. This is VERY DANGEROUS, and is
* intended for careful use by the ATAPI tape/cdrom driver code.
*
- * If action is ide_next, then the rq is queued immediately after
- * the currently-being-processed-request (if any), and the function
- * returns without waiting for the new rq to be completed. As above,
- * This is VERY DANGEROUS, and is intended for careful use by the
- * ATAPI tape/cdrom driver code.
- *
* If action is ide_end, then the rq is queued at the end of the
* request queue, and the function returns immediately without waiting
* for the new rq to be completed. This is again intended for careful
int where = ELEVATOR_INSERT_BACK, err;
int must_wait = (action == ide_wait || action == ide_head_wait);
-#ifdef CONFIG_BLK_DEV_PDC4030
- /*
- * FIXME: there should be a drive or hwif->special
- * handler that points here by default, not hacks
- * in the ide-io.c code
- *
- * FIXME2: That code breaks power management if used with
- * this chipset, that really doesn't belong here !
- */
- if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL)
- return -ENOSYS; /* special drive cmds not supported */
-#endif
rq->errors = 0;
rq->rq_status = RQ_ACTIVE;
- rq->rq_disk = drive->disk;
-
/*
* we need to hold an extra reference to request for safe inspection
* after completion
if (must_wait) {
rq->ref_count++;
rq->waiting = &wait;
+ rq->end_io = blk_end_sync_rq;
}
spin_lock_irqsave(&ide_lock, flags);
err = 0;
if (must_wait) {
wait_for_completion(&wait);
+ rq->waiting = NULL;
if (rq->errors)
err = -EIO;