Commit 9adda9b6 authored by Bartlomiej Zolnierkiewicz's avatar Bartlomiej Zolnierkiewicz Committed by Linus Torvalds

[PATCH] ide: bring non-taskfile code back

Unexpectedly there are some taskfile IO bugreports,
so bring back old code and add taskfile IO config option (default y).
This aids testing and lets people use safe(?) old code.
parent 508cf6ef
......@@ -219,6 +219,15 @@ config IDE_TASK_IOCTL
If you are unsure, say N here.
config IDE_TASKFILE_IO
bool 'IDE Taskfile IO'
depends on BLK_DEV_IDE
default y
---help---
Use new taskfile IO code.
It is safe to say Y to this question, in most cases.
comment "IDE chipset support/bugfixes"
depends on BLK_DEV_IDE
......
......@@ -136,6 +136,423 @@ static int idedisk_start_tag(ide_drive_t *drive, struct request *rq)
return ret;
}
#ifndef CONFIG_IDE_TASKFILE_IO
static int driver_blocked;
/*
* read_intr() is the handler for disk read/multread interrupts
*/
static ide_startstop_t read_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u32 i = 0, nsect = 0, msect = drive->mult_count;
struct request *rq;
unsigned long flags;
u8 stat;
char *to;
/* new way for dealing with premature shared PCI interrupts */
if (!OK_STAT(stat=hwif->INB(IDE_STATUS_REG),DATA_READY,BAD_R_STAT)) {
if (stat & (ERR_STAT|DRQ_STAT)) {
return DRIVER(drive)->error(drive, "read_intr", stat);
}
/* no data yet, so wait for another interrupt */
ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
return ide_started;
}
read_next:
rq = HWGROUP(drive)->rq;
if (msect) {
if ((nsect = rq->current_nr_sectors) > msect)
nsect = msect;
msect -= nsect;
} else
nsect = 1;
to = ide_map_buffer(rq, &flags);
taskfile_input_data(drive, to, nsect * SECTOR_WORDS);
#ifdef DEBUG
printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
drive->name, rq->sector, rq->sector+nsect-1,
(unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
#endif
ide_unmap_buffer(rq, to, &flags);
rq->sector += nsect;
rq->errors = 0;
i = (rq->nr_sectors -= nsect);
if (((long)(rq->current_nr_sectors -= nsect)) <= 0)
ide_end_request(drive, 1, rq->hard_cur_sectors);
/*
* Another BH Page walker and DATA INTEGRITY Questioned on ERROR.
* If passed back up on multimode read, BAD DATA could be ACKED
* to FILE SYSTEMS above ...
*/
if (i > 0) {
if (msect)
goto read_next;
ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
return ide_started;
}
return ide_stopped;
}
/*
* write_intr() is the handler for disk write interrupts
*/
static ide_startstop_t write_intr (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
ide_hwif_t *hwif = HWIF(drive);
struct request *rq = hwgroup->rq;
u32 i = 0;
u8 stat;
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),
DRIVE_READY, drive->bad_wstat)) {
printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n",
drive->name, rq->nr_sectors, stat);
} else {
#ifdef DEBUG
printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
drive->name, rq->sector, (unsigned long) rq->buffer,
rq->nr_sectors-1);
#endif
if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
rq->sector++;
rq->errors = 0;
i = --rq->nr_sectors;
--rq->current_nr_sectors;
if (((long)rq->current_nr_sectors) <= 0)
ide_end_request(drive, 1, rq->hard_cur_sectors);
if (i > 0) {
unsigned long flags;
char *to = ide_map_buffer(rq, &flags);
taskfile_output_data(drive, to, SECTOR_WORDS);
ide_unmap_buffer(rq, to, &flags);
ide_set_handler(drive, &write_intr, WAIT_CMD, NULL);
return ide_started;
}
return ide_stopped;
}
/* the original code did this here (?) */
return ide_stopped;
}
return DRIVER(drive)->error(drive, "write_intr", stat);
}
/*
* ide_multwrite() transfers a block of up to mcount sectors of data
* to a drive as part of a disk multiple-sector write operation.
*
* Returns 0 on success.
*
* Note that we may be called from two contexts - __ide_do_rw_disk() context
* and IRQ context. The IRQ can happen any time after we've output the
* full "mcount" number of sectors, so we must make sure we update the
* state _before_ we output the final part of the data!
*
* The update and return to BH is a BLOCK Layer Fakey to get more data
* to satisfy the hardware atomic segment. If the hardware atomic segment
* is shorter or smaller than the BH segment then we should be OKAY.
* This is only valid if we can rewind the rq->current_nr_sectors counter.
*/
int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = &hwgroup->wrq;
do {
char *buffer;
int nsect = rq->current_nr_sectors;
unsigned long flags;
if (nsect > mcount)
nsect = mcount;
mcount -= nsect;
buffer = ide_map_buffer(rq, &flags);
rq->sector += nsect;
rq->nr_sectors -= nsect;
rq->current_nr_sectors -= nsect;
/* Do we move to the next bh after this? */
if (!rq->current_nr_sectors) {
struct bio *bio = rq->bio;
/*
* only move to next bio, when we have processed
* all bvecs in this one.
*/
if (++bio->bi_idx >= bio->bi_vcnt) {
bio->bi_idx = 0;
bio = bio->bi_next;
}
/* end early early we ran out of requests */
if (!bio) {
mcount = 0;
} else {
rq->bio = bio;
rq->current_nr_sectors = bio_iovec(bio)->bv_len >> 9;
rq->hard_cur_sectors = rq->current_nr_sectors;
}
}
/*
* Ok, we're all setup for the interrupt
* re-entering us on the last transfer.
*/
taskfile_output_data(drive, buffer, nsect<<7);
ide_unmap_buffer(rq, buffer, &flags);
} while (mcount);
return 0;
}
/*
* multwrite_intr() is the handler for disk multwrite interrupts
*/
static ide_startstop_t multwrite_intr (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
ide_hwif_t *hwif = HWIF(drive);
struct request *rq = &hwgroup->wrq;
u8 stat;
stat = hwif->INB(IDE_STATUS_REG);
if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) {
if (stat & DRQ_STAT) {
/*
* The drive wants data. Remember rq is the copy
* of the request
*/
if (rq->nr_sectors) {
if (ide_multwrite(drive, drive->mult_count))
return ide_stopped;
ide_set_handler(drive, &multwrite_intr, WAIT_CMD, NULL);
return ide_started;
}
} else {
/*
* If the copy has all the blocks completed then
* we can end the original request.
*/
if (!rq->nr_sectors) { /* all done? */
rq = hwgroup->rq;
ide_end_request(drive, 1, rq->nr_sectors);
return ide_stopped;
}
}
/* the original code did this here (?) */
return ide_stopped;
}
return DRIVER(drive)->error(drive, "multwrite_intr", stat);
}
/*
* __ide_do_rw_disk() issues READ and WRITE commands to a disk,
* using LBA if supported, or CHS otherwise, to address sectors.
* It also takes care of issuing special DRIVE_CMDs.
*/
ide_startstop_t __ide_do_rw_disk (ide_drive_t *drive, struct request *rq, sector_t block)
{
ide_hwif_t *hwif = HWIF(drive);
u8 lba48 = (drive->addressing == 1) ? 1 : 0;
task_ioreg_t command = WIN_NOP;
ata_nsector_t nsectors;
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();
return ide_started;
}
if (IDE_CONTROL_REG)
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
if (drive->select.b.lba) {
if (drive->addressing == 1) {
task_ioreg_t tasklets[10];
if (blk_rq_tagged(rq)) {
tasklets[0] = nsectors.b.low;
tasklets[1] = nsectors.b.high;
tasklets[2] = rq->tag << 3;
tasklets[3] = 0;
} else {
tasklets[0] = 0;
tasklets[1] = 0;
tasklets[2] = nsectors.b.low;
tasklets[3] = nsectors.b.high;
}
tasklets[4] = (task_ioreg_t) block;
tasklets[5] = (task_ioreg_t) (block>>8);
tasklets[6] = (task_ioreg_t) (block>>16);
tasklets[7] = (task_ioreg_t) (block>>24);
if (sizeof(block) == 4) {
tasklets[8] = (task_ioreg_t) 0;
tasklets[9] = (task_ioreg_t) 0;
} else {
tasklets[8] = (task_ioreg_t)((u64)block >> 32);
tasklets[9] = (task_ioreg_t)((u64)block >> 40);
}
#ifdef DEBUG
printk("%s: %sing: LBAsect=%lu, sectors=%ld, "
"buffer=0x%08lx, LBAsect=0x%012lx\n",
drive->name,
rq_data_dir(rq)==READ?"read":"writ",
block,
rq->nr_sectors,
(unsigned long) rq->buffer,
block);
printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n",
drive->name, tasklets[3], tasklets[2],
tasklets[9], tasklets[8], tasklets[7],
tasklets[6], tasklets[5], tasklets[4]);
#endif
hwif->OUTB(tasklets[1], IDE_FEATURE_REG);
hwif->OUTB(tasklets[3], IDE_NSECTOR_REG);
hwif->OUTB(tasklets[7], IDE_SECTOR_REG);
hwif->OUTB(tasklets[8], IDE_LCYL_REG);
hwif->OUTB(tasklets[9], IDE_HCYL_REG);
hwif->OUTB(tasklets[0], IDE_FEATURE_REG);
hwif->OUTB(tasklets[2], IDE_NSECTOR_REG);
hwif->OUTB(tasklets[4], IDE_SECTOR_REG);
hwif->OUTB(tasklets[5], IDE_LCYL_REG);
hwif->OUTB(tasklets[6], IDE_HCYL_REG);
hwif->OUTB(0x00|drive->select.all,IDE_SELECT_REG);
} else {
#ifdef DEBUG
printk("%s: %sing: LBAsect=%llu, sectors=%ld, "
"buffer=0x%08lx\n",
drive->name,
rq_data_dir(rq)==READ?"read":"writ",
(unsigned long long)block, rq->nr_sectors,
(unsigned long) rq->buffer);
#endif
if (blk_rq_tagged(rq)) {
hwif->OUTB(nsectors.b.low, IDE_FEATURE_REG);
hwif->OUTB(rq->tag << 3, IDE_NSECTOR_REG);
} else {
hwif->OUTB(0x00, IDE_FEATURE_REG);
hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG);
}
hwif->OUTB(block, IDE_SECTOR_REG);
hwif->OUTB(block>>=8, IDE_LCYL_REG);
hwif->OUTB(block>>=8, IDE_HCYL_REG);
hwif->OUTB(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
}
} else {
unsigned int sect,head,cyl,track;
track = (int)block / drive->sect;
sect = (int)block % drive->sect + 1;
hwif->OUTB(sect, IDE_SECTOR_REG);
head = track % drive->head;
cyl = track / drive->head;
if (blk_rq_tagged(rq)) {
hwif->OUTB(nsectors.b.low, IDE_FEATURE_REG);
hwif->OUTB(rq->tag << 3, IDE_NSECTOR_REG);
} else {
hwif->OUTB(0x00, IDE_FEATURE_REG);
hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG);
}
hwif->OUTB(cyl, IDE_LCYL_REG);
hwif->OUTB(cyl>>8, IDE_HCYL_REG);
hwif->OUTB(head|drive->select.all,IDE_SELECT_REG);
#ifdef DEBUG
printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
drive->name, rq_data_dir(rq)==READ?"read":"writ", cyl,
head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
#endif
}
if (rq_data_dir(rq) == READ) {
if (blk_rq_tagged(rq))
return hwif->ide_dma_queued_read(drive);
if (drive->using_dma && !hwif->ide_dma_read(drive))
return ide_started;
command = ((drive->mult_count) ?
((lba48) ? WIN_MULTREAD_EXT : WIN_MULTREAD) :
((lba48) ? WIN_READ_EXT : WIN_READ));
ide_execute_command(drive, command, &read_intr, WAIT_CMD, NULL);
return ide_started;
} else if (rq_data_dir(rq) == WRITE) {
ide_startstop_t startstop;
if (blk_rq_tagged(rq))
return hwif->ide_dma_queued_write(drive);
if (drive->using_dma && !(HWIF(drive)->ide_dma_write(drive)))
return ide_started;
command = ((drive->mult_count) ?
((lba48) ? WIN_MULTWRITE_EXT : WIN_MULTWRITE) :
((lba48) ? WIN_WRITE_EXT : WIN_WRITE));
hwif->OUTB(command, IDE_COMMAND_REG);
if (ide_wait_stat(&startstop, drive, DATA_READY,
drive->bad_wstat, WAIT_DRQ)) {
printk(KERN_ERR "%s: no DRQ after issuing %s\n",
drive->name,
drive->mult_count ? "MULTWRITE" : "WRITE");
return startstop;
}
if (!drive->unmask)
local_irq_disable();
if (drive->mult_count) {
ide_hwgroup_t *hwgroup = HWGROUP(drive);
/*
* Ugh.. this part looks ugly because we MUST set up
* the interrupt handler before outputting the first block
* of data to be written. If we hit an error (corrupted buffer list)
* in ide_multwrite(), then we need to remove the handler/timer
* before returning. Fortunately, this NEVER happens (right?).
*
* Except when you get an error it seems...
*
* MAJOR DATA INTEGRITY BUG !!! only if we error
*/
hwgroup->wrq = *rq; /* scratchpad */
ide_set_handler(drive, &multwrite_intr, WAIT_CMD, NULL);
if (ide_multwrite(drive, drive->mult_count)) {
unsigned long flags;
spin_lock_irqsave(&ide_lock, flags);
hwgroup->handler = NULL;
del_timer(&hwgroup->timer);
spin_unlock_irqrestore(&ide_lock, flags);
return ide_stopped;
}
} else {
unsigned long flags;
char *to = ide_map_buffer(rq, &flags);
ide_set_handler(drive, &write_intr, WAIT_CMD, NULL);
taskfile_output_data(drive, to, SECTOR_WORDS);
ide_unmap_buffer(rq, to, &flags);
}
return ide_started;
}
blk_dump_rq_flags(rq, "__ide_do_rw_disk - bad command");
ide_end_request(drive, 0, 0);
return ide_stopped;
}
EXPORT_SYMBOL_GPL(__ide_do_rw_disk);
#else /* CONFIG_IDE_TASKFILE_IO */
static ide_startstop_t chs_rw_disk(ide_drive_t *, struct request *, unsigned long);
static ide_startstop_t lba_28_rw_disk(ide_drive_t *, struct request *, unsigned long);
static ide_startstop_t lba_48_rw_disk(ide_drive_t *, struct request *, unsigned long long);
......@@ -333,6 +750,8 @@ static ide_startstop_t lba_48_rw_disk (ide_drive_t *drive, struct request *rq, u
return do_rw_taskfile(drive, &args);
}
#endif /* CONFIG_IDE_TASKFILE_IO */
static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, sector_t block)
{
ide_hwif_t *hwif = HWIF(drive);
......@@ -447,8 +866,10 @@ ide_startstop_t idedisk_error (ide_drive_t *drive, const char *msg, u8 stat)
return ide_stopped;
}
#endif
#ifdef CONFIG_IDE_TASKFILE_IO
/* make rq completion pointers new submission pointers */
blk_rq_prep_restart(rq);
#endif
if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
/* other bits are useless when BUSY */
......
......@@ -164,6 +164,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
hwif->OUTB((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG);
#ifdef CONFIG_IDE_TASKFILE_IO
if (task->handler != NULL) {
if (task->prehandler != NULL) {
hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
......@@ -173,6 +174,14 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
return ide_started;
}
#else
if (task->handler != NULL) {
ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
if (task->prehandler != NULL)
return task->prehandler(drive, task->rq);
return ide_started;
}
#endif
if (!drive->using_dma)
return ide_stopped;
......@@ -352,6 +361,414 @@ ide_startstop_t task_no_data_intr (ide_drive_t *drive)
EXPORT_SYMBOL(task_no_data_intr);
/*
* old taskfile PIO handlers, to be killed as soon as possible.
*/
#ifndef CONFIG_IDE_TASKFILE_IO
#define task_map_rq(rq, flags) ide_map_buffer((rq), (flags))
#define task_unmap_rq(rq, buf, flags) ide_unmap_buffer((rq), (buf), (flags))
/*
* Handler for command with PIO data-in phase, READ
*/
/*
* FIXME before 2.4 enable ...
* DATA integrity issue upon error. <andre@linux-ide.org>
*/
ide_startstop_t task_in_intr (ide_drive_t *drive)
{
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
char *pBuf = NULL;
u8 stat;
unsigned long flags;
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),DATA_READY,BAD_R_STAT)) {
if (stat & (ERR_STAT|DRQ_STAT)) {
#if 0
DTF("%s: attempting to recover last " \
"sector counter status=0x%02x\n",
drive->name, stat);
/*
* Expect a BUG BOMB if we attempt to rewind the
* offset in the BH aka PAGE in the current BLOCK
* segment. This is different than the HOST segment.
*/
#endif
if (!rq->bio)
rq->current_nr_sectors++;
return DRIVER(drive)->error(drive, "task_in_intr", stat);
}
if (!(stat & BUSY_STAT)) {
DTF("task_in_intr to Soon wait for next interrupt\n");
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
}
#if 0
/*
* Holding point for a brain dump of a thought :-/
*/
if (!OK_STAT(stat,DRIVE_READY,drive->bad_wstat)) {
DTF("%s: READ attempting to recover last " \
"sector counter status=0x%02x\n",
drive->name, stat);
rq->current_nr_sectors++;
return DRIVER(drive)->error(drive, "task_in_intr", stat);
}
if (!rq->current_nr_sectors)
if (!DRIVER(drive)->end_request(drive, 1, 0))
return ide_stopped;
if (--rq->current_nr_sectors <= 0)
if (!DRIVER(drive)->end_request(drive, 1, 0))
return ide_stopped;
#endif
pBuf = task_map_rq(rq, &flags);
DTF("Read: %p, rq->current_nr_sectors: %d, stat: %02x\n",
pBuf, (int) rq->current_nr_sectors, stat);
taskfile_input_data(drive, pBuf, SECTOR_WORDS);
task_unmap_rq(rq, pBuf, &flags);
/*
* FIXME :: We really can not legally get a new page/bh
* regardless, if this is the end of our segment.
* BH walking or segment can only be updated after we have a good
* hwif->INB(IDE_STATUS_REG); return.
*/
if (--rq->current_nr_sectors <= 0)
if (!DRIVER(drive)->end_request(drive, 1, 0))
return ide_stopped;
/*
* ERM, it is techincally legal to leave/exit here but it makes
* a mess of the code ...
*/
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
EXPORT_SYMBOL(task_in_intr);
/*
* Handler for command with Read Multiple
*/
ide_startstop_t task_mulin_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct request *rq = HWGROUP(drive)->rq;
char *pBuf = NULL;
unsigned int msect = drive->mult_count;
unsigned int nsect;
unsigned long flags;
u8 stat;
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),DATA_READY,BAD_R_STAT)) {
if (stat & (ERR_STAT|DRQ_STAT)) {
if (!rq->bio) {
rq->current_nr_sectors += drive->mult_count;
/*
* NOTE: could rewind beyond beginning :-/
*/
} else {
printk(KERN_ERR "%s: MULTI-READ assume all data " \
"transfered is bad status=0x%02x\n",
drive->name, stat);
}
return DRIVER(drive)->error(drive, "task_mulin_intr", stat);
}
/* no data yet, so wait for another interrupt */
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_mulin_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
do {
nsect = rq->current_nr_sectors;
if (nsect > msect)
nsect = msect;
pBuf = task_map_rq(rq, &flags);
DTF("Multiread: %p, nsect: %d, msect: %d, " \
" rq->current_nr_sectors: %d\n",
pBuf, nsect, msect, rq->current_nr_sectors);
taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS);
task_unmap_rq(rq, pBuf, &flags);
rq->errors = 0;
rq->current_nr_sectors -= nsect;
msect -= nsect;
/*
* FIXME :: We really can not legally get a new page/bh
* regardless, if this is the end of our segment.
* BH walking or segment can only be updated after we have a
* good hwif->INB(IDE_STATUS_REG); return.
*/
if (!rq->current_nr_sectors) {
if (!DRIVER(drive)->end_request(drive, 1, 0))
return ide_stopped;
}
} while (msect);
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_mulin_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
EXPORT_SYMBOL(task_mulin_intr);
/*
* VERIFY ME before 2.4 ... unexpected race is possible based on details
* RMK with 74LS245/373/374 TTL buffer logic because of passthrough.
*/
ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq)
{
char *pBuf = NULL;
unsigned long flags;
ide_startstop_t startstop;
if (ide_wait_stat(&startstop, drive, DATA_READY,
drive->bad_wstat, WAIT_DRQ)) {
printk(KERN_ERR "%s: no DRQ after issuing WRITE%s\n",
drive->name,
drive->addressing ? "_EXT" : "");
return startstop;
}
/* For Write_sectors we need to stuff the first sector */
pBuf = task_map_rq(rq, &flags);
taskfile_output_data(drive, pBuf, SECTOR_WORDS);
rq->current_nr_sectors--;
task_unmap_rq(rq, pBuf, &flags);
return ide_started;
}
EXPORT_SYMBOL(pre_task_out_intr);
/*
* Handler for command with PIO data-out phase WRITE
*
* WOOHOO this is a CORRECT STATE DIAGRAM NOW, <andre@linux-ide.org>
*/
ide_startstop_t task_out_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct request *rq = HWGROUP(drive)->rq;
char *pBuf = NULL;
unsigned long flags;
u8 stat;
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG), DRIVE_READY, drive->bad_wstat)) {
DTF("%s: WRITE attempting to recover last " \
"sector counter status=0x%02x\n",
drive->name, stat);
rq->current_nr_sectors++;
return DRIVER(drive)->error(drive, "task_out_intr", stat);
}
/*
* Safe to update request for partial completions.
* We have a good STATUS CHECK!!!
*/
if (!rq->current_nr_sectors)
if (!DRIVER(drive)->end_request(drive, 1, 0))
return ide_stopped;
if ((rq->current_nr_sectors==1) ^ (stat & DRQ_STAT)) {
rq = HWGROUP(drive)->rq;
pBuf = task_map_rq(rq, &flags);
DTF("write: %p, rq->current_nr_sectors: %d\n",
pBuf, (int) rq->current_nr_sectors);
taskfile_output_data(drive, pBuf, SECTOR_WORDS);
task_unmap_rq(rq, pBuf, &flags);
rq->errors = 0;
rq->current_nr_sectors--;
}
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
EXPORT_SYMBOL(task_out_intr);
#undef ALTERNATE_STATE_DIAGRAM_MULTI_OUT
ide_startstop_t pre_task_mulout_intr (ide_drive_t *drive, struct request *rq)
{
#ifdef ALTERNATE_STATE_DIAGRAM_MULTI_OUT
ide_hwif_t *hwif = HWIF(drive);
char *pBuf = NULL;
unsigned int nsect = 0, msect = drive->mult_count;
u8 stat;
unsigned long flags;
#endif /* ALTERNATE_STATE_DIAGRAM_MULTI_OUT */
ide_task_t *args = rq->special;
ide_startstop_t startstop;
#if 0
/*
* assign private copy for multi-write
*/
memcpy(&HWGROUP(drive)->wrq, rq, sizeof(struct request));
#endif
if (ide_wait_stat(&startstop, drive, DATA_READY,
drive->bad_wstat, WAIT_DRQ)) {
printk(KERN_ERR "%s: no DRQ after issuing %s\n",
drive->name,
drive->addressing ? "MULTWRITE_EXT" : "MULTWRITE");
return startstop;
}
#ifdef ALTERNATE_STATE_DIAGRAM_MULTI_OUT
do {
nsect = rq->current_nr_sectors;
if (nsect > msect)
nsect = msect;
pBuf = task_map_rq(rq, &flags);
DTF("Pre-Multiwrite: %p, nsect: %d, msect: %d, " \
"rq->current_nr_sectors: %ld\n",
pBuf, nsect, msect, rq->current_nr_sectors);
msect -= nsect;
taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS);
task_unmap_rq(rq, pBuf, &flags);
rq->current_nr_sectors -= nsect;
if (!rq->current_nr_sectors) {
if (!DRIVER(drive)->end_request(drive, 1, 0))
if (!rq->bio) {
stat = hwif->INB(IDE_STATUS_REG);
return ide_stopped;
}
}
} while (msect);
rq->errors = 0;
return ide_started;
#else /* ! ALTERNATE_STATE_DIAGRAM_MULTI_OUT */
if (!(drive_is_ready(drive))) {
int i;
for (i=0; i<100; i++) {
if (drive_is_ready(drive))
break;
}
}
/*
* WARNING :: if the drive as not acked good status we may not
* move the DATA-TRANSFER T-Bar as BSY != 0. <andre@linux-ide.org>
*/
return args->handler(drive);
#endif /* ALTERNATE_STATE_DIAGRAM_MULTI_OUT */
}
EXPORT_SYMBOL(pre_task_mulout_intr);
/*
* FIXME before enabling in 2.4 ... DATA integrity issue upon error.
*/
/*
* Handler for command write multiple
* Called directly from execute_drive_cmd for the first bunch of sectors,
* afterwards only by the ISR
*/
ide_startstop_t task_mulout_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 stat = hwif->INB(IDE_STATUS_REG);
struct request *rq = HWGROUP(drive)->rq;
char *pBuf = NULL;
ide_startstop_t startstop = ide_stopped;
unsigned int msect = drive->mult_count;
unsigned int nsect;
unsigned long flags;
/*
* (ks/hs): Handle last IRQ on multi-sector transfer,
* occurs after all data was sent in this chunk
*/
if (rq->current_nr_sectors == 0) {
if (stat & (ERR_STAT|DRQ_STAT)) {
if (!rq->bio) {
rq->current_nr_sectors += drive->mult_count;
/*
* NOTE: could rewind beyond beginning :-/
*/
} else {
printk(KERN_ERR "%s: MULTI-WRITE assume all data " \
"transfered is bad status=0x%02x\n",
drive->name, stat);
}
return DRIVER(drive)->error(drive, "task_mulout_intr", stat);
}
if (!rq->bio)
DRIVER(drive)->end_request(drive, 1, 0);
return startstop;
}
/*
* DON'T be lazy code the above and below togather !!!
*/
if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) {
if (stat & (ERR_STAT|DRQ_STAT)) {
if (!rq->bio) {
rq->current_nr_sectors += drive->mult_count;
/*
* NOTE: could rewind beyond beginning :-/
*/
} else {
printk("%s: MULTI-WRITE assume all data " \
"transfered is bad status=0x%02x\n",
drive->name, stat);
}
return DRIVER(drive)->error(drive, "task_mulout_intr", stat);
}
/* no data yet, so wait for another interrupt */
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_mulout_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
#ifndef ALTERNATE_STATE_DIAGRAM_MULTI_OUT
if (HWGROUP(drive)->handler != NULL) {
unsigned long lflags;
spin_lock_irqsave(&ide_lock, lflags);
HWGROUP(drive)->handler = NULL;
del_timer(&HWGROUP(drive)->timer);
spin_unlock_irqrestore(&ide_lock, lflags);
}
#endif /* ALTERNATE_STATE_DIAGRAM_MULTI_OUT */
do {
nsect = rq->current_nr_sectors;
if (nsect > msect)
nsect = msect;
pBuf = task_map_rq(rq, &flags);
DTF("Multiwrite: %p, nsect: %d, msect: %d, " \
"rq->current_nr_sectors: %ld\n",
pBuf, nsect, msect, rq->current_nr_sectors);
msect -= nsect;
taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS);
task_unmap_rq(rq, pBuf, &flags);
rq->current_nr_sectors -= nsect;
/*
* FIXME :: We really can not legally get a new page/bh
* regardless, if this is the end of our segment.
* BH walking or segment can only be updated after we
* have a good hwif->INB(IDE_STATUS_REG); return.
*/
if (!rq->current_nr_sectors) {
if (!DRIVER(drive)->end_request(drive, 1, 0))
if (!rq->bio)
return ide_stopped;
}
} while (msect);
rq->errors = 0;
if (HWGROUP(drive)->handler == NULL)
ide_set_handler(drive, &task_mulout_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
EXPORT_SYMBOL(task_mulout_intr);
#else /* !CONFIG_IDE_TASKFILE_IO */
static u8 wait_drive_not_busy(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
......@@ -617,6 +1034,8 @@ ide_startstop_t pre_task_mulout_intr (ide_drive_t *drive, struct request *rq)
}
EXPORT_SYMBOL(pre_task_mulout_intr);
#endif /* !CONFIG_IDE_TASKFILE_IO */
/* Called by internal to feature out type of command being called */
//ide_pre_handler_t * ide_pre_handler_parser (task_struct_t *taskfile, hob_struct_t *hobfile)
ide_pre_handler_t * ide_pre_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile)
......
......@@ -439,6 +439,7 @@ static ide_startstop_t promise_read_intr (ide_drive_t *drive)
(unsigned long)rq->nr_sectors - nsect);
#endif /* DEBUG_READ */
#ifdef CONFIG_IDE_TASKFILE_IO
task_sectors(drive, rq, nsect, IDE_PIO_IN);
/* FIXME: can we check status after transfer on pdc4030? */
......@@ -446,6 +447,16 @@ static ide_startstop_t promise_read_intr (ide_drive_t *drive)
while (rq->bio != rq->cbio)
if (!DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio)))
return ide_stopped;
#else /* CONFIG_IDE_TASKFILE_IO */
HWIF(drive)->ata_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
rq->buffer += nsect<<9;
rq->sector += nsect;
rq->errors = 0;
rq->nr_sectors -= nsect;
if (!rq->current_nr_sectors)
DRIVER(drive)->end_request(drive, 1, 0);
#endif /* CONFIG_IDE_TASKFILE_IO */
/*
* Now the data has been read in, do the following:
*
......@@ -519,9 +530,13 @@ static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive)
printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
#endif /* DEBUG_WRITE */
#ifdef CONFIG_IDE_TASKFILE_IO
/* Complete previously submitted bios. */
while (rq->bio != rq->cbio)
(void) DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio));
#else
DRIVER(drive)->end_request(drive, 1, rq->hard_nr_sectors);
#endif
return ide_stopped;
}
......@@ -529,6 +544,7 @@ static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive)
* promise_multwrite() transfers a block of up to mcount sectors of data
* to a drive as part of a disk multiple-sector write operation.
*/
#ifdef CONFIG_IDE_TASKFILE_IO
static void promise_multwrite (ide_drive_t *drive, unsigned int msect)
{
struct request* rq = HWGROUP(drive)->rq;
......@@ -548,6 +564,59 @@ static void promise_multwrite (ide_drive_t *drive, unsigned int msect)
msect -= nsect;
} while (msect);
}
#else /* CONFIG_IDE_TASKFILE_IO */
static void promise_multwrite (ide_drive_t *drive, unsigned int mcount)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = &hwgroup->wrq;
do {
char *buffer;
int nsect = rq->current_nr_sectors;
if (nsect > mcount)
nsect = mcount;
mcount -= nsect;
buffer = rq->buffer;
rq->sector += nsect;
rq->buffer += nsect << 9;
rq->nr_sectors -= nsect;
rq->current_nr_sectors -= nsect;
/* Do we move to the next bh after this? */
if (!rq->current_nr_sectors) {
struct bio *bio = rq->bio;
/*
* only move to next bio, when we have processed
* all bvecs in this one.
*/
if (++bio->bi_idx >= bio->bi_vcnt) {
bio->bi_idx = 0;
bio = bio->bi_next;
}
/* end early early we ran out of requests */
if (!bio) {
mcount = 0;
} else {
rq->bio = bio;
rq->current_nr_sectors = bio_iovec(bio)->bv_len >> 9;
rq->hard_cur_sectors = rq->current_nr_sectors;
}
}
/*
* Ok, we're all setup for the interrupt
* re-entering us on the last transfer.
*/
taskfile_output_data(drive, buffer, nsect<<7);
} while (mcount);
return 0;
}
#endif
/*
* promise_write_pollfunc() is the handler for disk write completion polling.
......@@ -573,9 +642,11 @@ static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
HWIF(drive)->INB(IDE_STATUS_REG));
}
#ifdef CONFIG_IDE_TASKFILE_IO
/* Complete previously submitted bios. */
while (rq->bio != rq->cbio)
(void) DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio));
#endif
/*
* Now write out last 4 sectors and poll for not BUSY
......@@ -602,7 +673,11 @@ static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
static ide_startstop_t promise_write (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
#ifdef CONFIG_IDE_TASKFILE_IO
struct request *rq = hwgroup->rq;
#else
struct request *rq = &hwgroup->wrq;
#endif
#ifdef DEBUG_WRITE
printk(KERN_DEBUG "%s: %s: sectors(%lu-%lu)\n",
......@@ -650,6 +725,13 @@ static ide_startstop_t promise_write (ide_drive_t *drive)
* already set up. It issues a READ or WRITE command to the Promise
* controller, assuming LBA has been used to set up the block number.
*/
#ifndef CONFIG_IDE_TASKFILE_IO
ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
unsigned long timeout;
u8 stat = 0;
#else
static ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task)
{
struct request *rq = HWGROUP(drive)->rq;
......@@ -670,8 +752,12 @@ static ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task)
HWIF(drive)->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
HWIF(drive)->OUTB(taskfile->device_head, IDE_SELECT_REG);
HWIF(drive)->OUTB(taskfile->command, IDE_COMMAND_REG);
#endif
if (rq_data_dir(rq) == READ) {
#ifndef CONFIG_IDE_TASKFILE_IO
HWIF(drive)->OUTB(PROMISE_READ, IDE_COMMAND_REG);
#endif
/*
* The card's behaviour is odd at this point. If the data is
* available, DRQ will be true, and no interrupt will be
......@@ -707,6 +793,9 @@ static ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task)
"waiting - Odd!\n", drive->name);
return ide_stopped;
} else {
#ifndef CONFIG_IDE_TASKFILE_IO
HWIF(drive)->OUTB(PROMISE_WRITE, IDE_COMMAND_REG);
#endif
if (ide_wait_stat(&startstop, drive, DATA_READY,
drive->bad_wstat, WAIT_DRQ)) {
printk(KERN_ERR "%s: no DRQ after issuing "
......@@ -715,6 +804,9 @@ static ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task)
}
if (!drive->unmask)
local_irq_disable();
#ifndef CONFIG_IDE_TASKFILE_IO
HWGROUP(drive)->wrq = *rq; /* scratchpad */
#endif
return promise_write(drive);
}
}
......@@ -727,8 +819,10 @@ static ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq,
FIXME: Is promise_selectproc now redundant??
*/
int drive_number = (HWIF(drive)->channel << 1) + drive->select.b.unit;
#ifdef CONFIG_IDE_TASKFILE_IO
struct hd_drive_task_hdr taskfile;
ide_task_t args;
#endif
BUG_ON(rq->nr_sectors > 127);
......@@ -744,6 +838,18 @@ static ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq,
block, rq->nr_sectors);
#endif
#ifndef CONFIG_IDE_TASKFILE_IO
if (IDE_CONTROL_REG)
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
hwif->OUTB(drive_number, IDE_FEATURE_REG);
hwif->OUTB(rq->nr_sectors, IDE_NSECTOR_REG);
hwif->OUTB(block,IDE_SECTOR_REG);
hwif->OUTB(block>>=8,IDE_LCYL_REG);
hwif->OUTB(block>>=8,IDE_HCYL_REG);
hwif->OUTB(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
return do_pdc4030_io(drive, rq);
#else /* !CONFIG_IDE_TASKFILE_IO */
memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr));
taskfile.feature = drive_number;
......@@ -766,4 +872,5 @@ static ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq,
rq->special = (ide_task_t *)&args;
return do_pdc4030_io(drive, &args);
#endif /* !CONFIG_IDE_TASKFILE_IO */
}
......@@ -817,6 +817,38 @@ typedef struct ide_dma_ops_s {
*
* temporarily mapping a (possible) highmem bio for PIO transfer
*/
#ifndef CONFIG_IDE_TASKFILE_IO
#define ide_rq_offset(rq) \
(((rq)->hard_cur_sectors - (rq)->current_nr_sectors) << 9)
/*
* taskfiles really should use hard_cur_sectors as well!
*/
#define task_rq_offset(rq) \
(((rq)->nr_sectors - (rq)->current_nr_sectors) * SECTOR_SIZE)
static inline void *ide_map_buffer(struct request *rq, unsigned long *flags)
{
/*
* fs request
*/
if (rq->bio)
return bio_kmap_irq(rq->bio, flags) + ide_rq_offset(rq);
/*
* task request
*/
return rq->buffer + task_rq_offset(rq);
}
static inline void ide_unmap_buffer(struct request *rq, char *buffer, unsigned long *flags)
{
if (rq->bio)
bio_kunmap_irq(buffer, flags);
}
#else /* !CONFIG_IDE_TASKFILE_IO */
static inline void *task_map_rq(struct request *rq, unsigned long *flags)
{
......@@ -838,6 +870,8 @@ static inline void task_unmap_rq(struct request *rq, char *buffer, unsigned long
rq_unmap_buffer(buffer, flags);
}
#endif /* !CONFIG_IDE_TASKFILE_IO */
#define IDE_CHIPSET_PCI_MASK \
((1<<ide_pci)|(1<<ide_cmd646)|(1<<ide_ali14xx))
#define IDE_CHIPSET_IS_PCI(c) ((IDE_CHIPSET_PCI_MASK >> (c)) & 1)
......@@ -1410,6 +1444,8 @@ extern void atapi_output_bytes(ide_drive_t *, void *, u32);
extern void taskfile_input_data(ide_drive_t *, void *, u32);
extern void taskfile_output_data(ide_drive_t *, void *, u32);
#ifdef CONFIG_IDE_TASKFILE_IO
#define IDE_PIO_IN 0
#define IDE_PIO_OUT 1
......@@ -1435,6 +1471,8 @@ static inline void task_sectors(ide_drive_t *drive, struct request *rq,
task_unmap_rq(rq, buf, &flags);
}
#endif /* CONFIG_IDE_TASKFILE_IO */
extern int drive_is_ready(ide_drive_t *);
extern int wait_for_ready(ide_drive_t *, int /* timeout */);
......
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