* Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
* Copyright (C) 2001-2002 Klaus Smolin
* IBM Storage Technology Division
- * Copyright (C) 2003 Bartlomiej Zolnierkiewicz
+ * Copyright (C) 2003-2004 Bartlomiej Zolnierkiewicz
*
* The big the bad and the ugly.
*
#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
+#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/bitops.h>
#define DEBUG_TASKFILE 0 /* unset when fixed */
}
}
-
-void taskfile_input_data (ide_drive_t *drive, void *buffer, u32 wcount)
+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);
}
-EXPORT_SYMBOL(taskfile_input_data);
-
-void taskfile_output_data (ide_drive_t *drive, void *buffer, u32 wcount)
+static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount)
{
if (drive->bswap) {
ata_bswap_data(buffer, wcount);
}
}
-EXPORT_SYMBOL(taskfile_output_data);
-
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
{
ide_task_t args;
return ide_raw_taskfile(drive, &args, buf);
}
-EXPORT_SYMBOL(taskfile_lib_get_identify);
-
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
{
ide_hwif_t *hwif = HWIF(drive);
case WIN_WRITEDMA_ONCE:
case WIN_WRITEDMA:
case WIN_WRITEDMA_EXT:
- if (!hwif->ide_dma_write(drive))
- return ide_started;
- break;
case WIN_READDMA_ONCE:
case WIN_READDMA:
case WIN_READDMA_EXT:
case WIN_IDENTIFY_DMA:
- if (!hwif->ide_dma_read(drive))
+ 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;
if (stat & (ERR_STAT|DRQ_STAT))
- return DRIVER(drive)->error(drive, "set_geometry_intr", stat);
+ return ide_error(drive, "set_geometry_intr", stat);
if (HWGROUP(drive)->handler != NULL)
BUG();
u8 stat;
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG), READY_STAT, BAD_STAT))
- return DRIVER(drive)->error(drive, "recal_intr", stat);
+ return ide_error(drive, "recal_intr", stat);
return ide_stopped;
}
local_irq_enable();
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),READY_STAT,BAD_STAT)) {
- return DRIVER(drive)->error(drive, "task_no_data_intr", stat);
+ return ide_error(drive, "task_no_data_intr", stat);
/* calls ide_end_drive_cmd */
}
if (args)
EXPORT_SYMBOL(task_no_data_intr);
-static void task_buffer_sectors(ide_drive_t *drive, struct request *rq,
- unsigned nsect, unsigned rw)
-{
- char *buf = rq->buffer + blk_rq_offset(rq);
-
- rq->sector += nsect;
- rq->current_nr_sectors -= nsect;
- rq->nr_sectors -= nsect;
- __task_sectors(drive, buf, nsect, rw);
-}
-
-static inline void task_buffer_multi_sectors(ide_drive_t *drive,
- struct request *rq, unsigned rw)
-{
- unsigned int msect = drive->mult_count, nsect;
-
- nsect = rq->current_nr_sectors;
- if (nsect > msect)
- nsect = msect;
-
- task_buffer_sectors(drive, rq, nsect, rw);
-}
-
-#ifdef CONFIG_IDE_TASKFILE_IO
-static void task_sectors(ide_drive_t *drive, struct request *rq,
- unsigned nsect, unsigned rw)
-{
- if (rq->cbio) { /* fs request */
- rq->errors = 0;
- task_bio_sectors(drive, rq, nsect, rw);
- } else /* task request */
- task_buffer_sectors(drive, rq, nsect, rw);
-}
-
-static inline void task_bio_multi_sectors(ide_drive_t *drive,
- struct request *rq, unsigned rw)
-{
- unsigned int nsect, msect = drive->mult_count;
-
- do {
- nsect = rq->current_nr_sectors;
- if (nsect > msect)
- nsect = msect;
-
- task_bio_sectors(drive, rq, nsect, rw);
-
- if (!rq->nr_sectors)
- msect = 0;
- else
- msect -= nsect;
- } while (msect);
-}
-
-static void task_multi_sectors(ide_drive_t *drive,
- struct request *rq, unsigned rw)
-{
- if (rq->cbio) { /* fs request */
- rq->errors = 0;
- task_bio_multi_sectors(drive, rq, rw);
- } else /* task request */
- task_buffer_multi_sectors(drive, rq, rw);
-}
-#else
-# define task_sectors(d, rq, nsect, rw) task_buffer_sectors(d, rq, nsect, rw)
-# define task_multi_sectors(d, rq, rw) task_buffer_multi_sectors(d, rq, rw)
-#endif /* CONFIG_IDE_TASKFILE_IO */
-
static u8 wait_drive_not_busy(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
return stat;
}
+static void ide_pio_sector(ide_drive_t *drive, unsigned int write)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct scatterlist *sg = hwif->sg_table;
+ struct page *page;
+#ifdef CONFIG_HIGHMEM
+ unsigned long flags;
+#endif
+ unsigned int offset;
+ u8 *buf;
+
+ page = sg[hwif->cursg].page;
+ offset = sg[hwif->cursg].offset + hwif->cursg_ofs * SECTOR_SIZE;
+
+ /* get the current page and offset */
+ page = nth_page(page, (offset >> PAGE_SHIFT));
+ offset %= PAGE_SIZE;
+
+#ifdef CONFIG_HIGHMEM
+ local_irq_save(flags);
+#endif
+ buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset;
+
+ hwif->nleft--;
+ hwif->cursg_ofs++;
+
+ if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) {
+ hwif->cursg++;
+ hwif->cursg_ofs = 0;
+ }
+
+ /* do the actual data transfer */
+ if (write)
+ taskfile_output_data(drive, buf, SECTOR_WORDS);
+ else
+ taskfile_input_data(drive, buf, SECTOR_WORDS);
+
+ kunmap_atomic(buf, KM_BIO_SRC_IRQ);
+#ifdef CONFIG_HIGHMEM
+ local_irq_restore(flags);
+#endif
+}
+
+static void ide_pio_multi(ide_drive_t *drive, unsigned int write)
+{
+ unsigned int nsect;
+
+ nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count);
+ while (nsect--)
+ ide_pio_sector(drive, write);
+}
+
static inline void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
unsigned int write)
{
+ if (rq->bio) /* fs request */
+ rq->errors = 0;
+
switch (drive->hwif->data_phase) {
case TASKFILE_MULTI_IN:
case TASKFILE_MULTI_OUT:
- task_multi_sectors(drive, rq, write);
+ ide_pio_multi(drive, write);
break;
default:
- task_sectors(drive, rq, 1, write);
+ ide_pio_sector(drive, write);
break;
}
}
-#ifdef CONFIG_IDE_TASKFILE_IO
static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq,
const char *s, u8 stat)
{
if (rq->bio) {
- int sectors = rq->hard_nr_sectors - rq->nr_sectors;
+ ide_hwif_t *hwif = drive->hwif;
+ int sectors = hwif->nsect - hwif->nleft;
- switch (drive->hwif->data_phase) {
+ switch (hwif->data_phase) {
case TASKFILE_IN:
- if (rq->nr_sectors)
+ if (hwif->nleft)
break;
/* fall through */
case TASKFILE_OUT:
sectors--;
break;
case TASKFILE_MULTI_IN:
- if (rq->nr_sectors)
+ if (hwif->nleft)
break;
/* fall through */
case TASKFILE_MULTI_OUT:
if (sectors > 0)
drive->driver->end_request(drive, 1, sectors);
}
- return drive->driver->error(drive, s, stat);
+ return ide_error(drive, s, stat);
}
-#else
-# define task_error(d, rq, s, stat) drive->driver->error(d, s, stat)
-#endif
static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
{
*/
ide_startstop_t task_in_intr (ide_drive_t *drive)
{
+ ide_hwif_t *hwif = drive->hwif;
struct request *rq = HWGROUP(drive)->rq;
- u8 stat = HWIF(drive)->INB(IDE_STATUS_REG);
+ u8 stat = hwif->INB(IDE_STATUS_REG);
+ /* new way for dealing with premature shared PCI interrupts */
if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) {
if (stat & (ERR_STAT | DRQ_STAT))
return task_error(drive, rq, __FUNCTION__, stat);
ide_pio_datablock(drive, rq, 0);
/* If it was the last datablock check status and finish transfer. */
- if (!rq->nr_sectors) {
+ if (!hwif->nleft) {
stat = wait_drive_not_busy(drive);
if (!OK_STAT(stat, 0, BAD_R_STAT))
return task_error(drive, rq, __FUNCTION__, stat);
/*
* Handler for command with PIO data-out phase (Write/Write Multiple).
*/
-ide_startstop_t task_out_intr (ide_drive_t *drive)
+static ide_startstop_t task_out_intr (ide_drive_t *drive)
{
+ ide_hwif_t *hwif = drive->hwif;
struct request *rq = HWGROUP(drive)->rq;
- u8 stat;
+ u8 stat = hwif->INB(IDE_STATUS_REG);
- stat = HWIF(drive)->INB(IDE_STATUS_REG);
if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
return task_error(drive, rq, __FUNCTION__, stat);
/* Deal with unexpected ATA data phase. */
- if (((stat & DRQ_STAT) == 0) ^ !rq->nr_sectors)
+ if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft)
return task_error(drive, rq, __FUNCTION__, stat);
- if (!rq->nr_sectors) {
+ if (!hwif->nleft) {
task_end_request(drive, rq, stat);
return ide_stopped;
}
return ide_started;
}
-EXPORT_SYMBOL(task_out_intr);
-
ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
}
EXPORT_SYMBOL(pre_task_out_intr);
-int ide_diag_taskfile (ide_drive_t *drive, ide_task_t *args, unsigned long data_size, u8 *buf)
+static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long data_size, u8 *buf)
{
struct request rq;
rq.hard_nr_sectors = rq.nr_sectors;
rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors;
+
+ if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
+ rq.flags |= REQ_RW;
}
rq.special = args;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
-EXPORT_SYMBOL(ide_diag_taskfile);
-
int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, u8 *buf)
{
return ide_diag_taskfile(drive, args, 0, buf);
EXPORT_SYMBOL(ide_raw_taskfile);
-#define MAX_DMA (256*SECTOR_WORDS)
-
-ide_startstop_t flagged_taskfile(ide_drive_t *, ide_task_t *);
-
int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
{
ide_task_request_t *req_task;
return err;
}
-EXPORT_SYMBOL(ide_taskfile_ioctl);
-
int ide_wait_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, u8 feature, u8 sectors, u8 *buf)
{
struct request rq;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
-EXPORT_SYMBOL(ide_wait_cmd);
-
/*
* FIXME : this needs to map into at taskfile. <andre@linux-ide.org>
*/
return err;
}
-EXPORT_SYMBOL(ide_cmd_ioctl);
-
-int ide_wait_cmd_task (ide_drive_t *drive, u8 *buf)
+static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf)
{
struct request rq;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
-EXPORT_SYMBOL(ide_wait_cmd_task);
-
/*
* FIXME : this needs to map into at taskfile. <andre@linux-ide.org>
*/
return err;
}
-EXPORT_SYMBOL(ide_task_ioctl);
-
/*
* NOTICE: This is additions from IBM to provide a discrete interface,
* for selective taskregister access operations. Nice JOB Klaus!!!
hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
/*
- * (ks) In the flagged taskfile approch, we will used all specified
- * registers and the register value will not be changed. Except the
+ * (ks) In the flagged taskfile approch, we will use all specified
+ * registers and the register value will not be changed, except the
* select bit (master/slave) in the drive_head register. We must make
* sure that the desired drive is selected.
*/
case TASKFILE_OUT_DMAQ:
case TASKFILE_OUT_DMA:
- hwif->ide_dma_write(drive);
- break;
-
case TASKFILE_IN_DMAQ:
case TASKFILE_IN_DMA:
- hwif->ide_dma_read(drive);
+ hwif->dma_setup(drive);
+ hwif->dma_exec_cmd(drive, taskfile->command);
+ hwif->dma_start(drive);
break;
default:
return ide_started;
}
-
-EXPORT_SYMBOL(flagged_taskfile);