/*
* linux/drivers/ide/ide-taskfile.c Version 0.38 March 05, 2003
*
* Copyright (C) 2000-2002 Michael Cornwell <cornwell@acm.org>
* Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
* Copyright (C) 2001-2002 Klaus Smolin
* IBM Storage Technology Division
* Copyright (C) 2003-2004 Bartlomiej Zolnierkiewicz
*
* The big the bad and the ugly.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/genhd.h>
#include <linux/blkpg.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/bitops.h>
#include <linux/scatterlist.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
static void ata_bswap_data (void *buffer, int wcount)
{
u16 *p = buffer;
while (wcount--) {
*p = *p << 8 | *p >> 8; p++;
*p = *p << 8 | *p >> 8; p++;
}
}
static void taskfile_input_data(ide_drive_t *drive, void *buffer, u32 wcount)
{
HWIF(drive)->ata_input_data(drive, buffer, wcount);
if (drive->bswap)
ata_bswap_data(buffer, wcount);
}
static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount)
{
if (drive->bswap) {
ata_bswap_data(buffer, wcount);
HWIF(drive)->ata_output_data(drive, buffer, wcount);
ata_bswap_data(buffer, wcount);
} else {
HWIF(drive)->ata_output_data(drive, buffer, wcount);
}
}
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
{
ide_task_t args;
memset(&args, 0, sizeof(ide_task_t));
args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01;
if (drive->media == ide_disk)
args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDENTIFY;
else
args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY;
args.command_type = IDE_DRIVE_TASK_IN;
args.data_phase = TASKFILE_IN;
args.handler = &task_in_intr;
return ide_raw_taskfile(drive, &args, buf);
}
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
{
ide_hwif_t *hwif = HWIF(drive);
task_struct_t *taskfile = (task_struct_t *) task->tfRegister;
hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister;
u8 HIHI = (drive->addressing == 1) ? 0xE0 : 0xEF;
/* ALL Command Block Executions SHALL clear nIEN, unless otherwise */
if (IDE_CONTROL_REG) {
/* clear nIEN */
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
}
SELECT_MASK(drive, 0);
if (drive->addressing == 1) {
hwif->OUTB(hobfile->feature, IDE_FEATURE_REG);
hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG);
hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG);
hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG);
hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG);
}
hwif->OUTB(taskfile->feature, IDE_FEATURE_REG);
hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG);
hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG);
hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG);
hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
hwif->OUTB((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG);
if (task->handler != NULL) {
if (task->prehandler != NULL) {
hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
ndelay(400); /* FIXME */
return task->prehandler(drive, task->rq);
}
ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
return ide_started;
}
if (!drive->using_dma)
return ide_stopped;
switch (taskfile->command) {
case WIN_WRITEDMA_ONCE:
case WIN_WRITEDMA:
case WIN_WRITEDMA_EXT:
case WIN_READDMA_ONCE:
case WIN_READDMA:
case WIN_READDMA_EXT:
case WIN_IDENTIFY_DMA:
if (!hwif->dma_setup(drive)) {
hwif->dma_exec_cmd(drive, taskfile->command);
hwif->dma_start(drive);
return ide_started;
}
break;
default:
if (task->handler == NULL)
return ide_stopped;
}
return ide_stopped;
}
/*
* set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
*/
ide_startstop_t set_multmode_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 stat;
if (OK_STAT(stat = hwif->INB(IDE_STATUS_REG),READY_STAT,BAD_STAT