linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
[parent not found: <BFECAF9E178F144FAEF2BF4CE739C668030176E7@exmail1.se.axis.com>]
[parent not found: <BFECAF9E178F144FAEF2BF4CE739C668030174FD@exmail1.se.axis.com>]
* New IDE driver for review
@ 2005-06-09  7:18 Mikael Starvik
  2005-06-09 10:14 ` Bartlomiej Zolnierkiewicz
  0 siblings, 1 reply; 9+ messages in thread
From: Mikael Starvik @ 2005-06-09  7:18 UTC (permalink / raw)
  To: linux-ide

[-- Attachment #1: Type: text/plain, Size: 457 bytes --]

I am about to release a new subarchitecture to the cris architcecture.
A part of this release is a new IDE driver. Basically just a port of
the IDE driver for the old architecture with the addition of UDMA 0-2.

The driver is attached if anyone wants to review it before submission.
I understand if no one is interrested in taking a look at it (CRIS
is really a minor arch).

If no one objects I'll send this to Andrew after the 2.6.12 release.

/Mikael

 

[-- Attachment #2: ide-v32.c --]
[-- Type: application/octet-stream, Size: 24066 bytes --]

/* $Id: ide-v32.c,v 1.5 2005/06/09 07:11:54 starvik Exp $
 *
 * Etrax specific IDE functions, like init and PIO-mode setting etc.
 * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
 * Copyright (c) 2000-2004 Axis Communications AB
 *
 * Authors:    Bjorn Wesen        (initial version)
 *             Mikael Starvik     (crisv32 port)
 */

/* Regarding DMA:
 *
 * There are two forms of DMA - "DMA handshaking" between the interface and the drive,
 * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's
 * something built-in in the Etrax. However only some drives support the DMA-mode handshaking
 * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the
 * device can't do DMA handshaking for some stupid reason. We don't need to do that.
 */

#undef REALLY_SLOW_IO           /* most systems can safely undef this */

#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>

#include <asm/io.h>
#include <asm/arch/hwregs/ata_defs.h>
#include <asm/arch/hwregs/dma_defs.h>
#include <asm/arch/hwregs/dma.h>
#include <asm/dma.h>
#include <asm/arch/pinmux.h>

/* number of DMA descriptors */
#define MAX_DMA_DESCRS 64

/* number of times to retry busy-flags when reading/writing IDE-registers
 * this can't be too high because a hung harddisk might cause the watchdog
 * to trigger (sometimes INB and OUTB are called with irq's disabled)
 */

#define IDE_REGISTER_TIMEOUT 300

#define LOWDB(x) 
#define D(x) 

int crisv32_ide_ack_intr(ide_hwif_t* hwif)
{
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, 
	                         int, hwif->io_ports[0]);
	REG_WR_INT(ata, regi_ata, rw_ack_intr, 1 << ctrl2.sel);
	return 1;
}
void
crisv32_ide_outw(unsigned short data, unsigned long reg) {
	int timeleft;
	reg_ata_rs_stat_data stat_data;

	LOWDB(printk("ow: data 0x%x, reg 0x%x\n", data, reg));

	/* note the lack of handling any timeouts. we stop waiting, but we don't
	 * really notify anybody.
	 */

	timeleft = IDE_REGISTER_TIMEOUT;
	/* wait for busy flag */
	do {
		timeleft--;
		stat_data = REG_RD(ata, regi_ata, rs_stat_data);
	} while(timeleft && stat_data.busy);

	/*
	 * Fall through at a timeout, so the ongoing command will be
	 * aborted by the write below, which is expected to be a dummy
	 * command to the command register.  This happens when a faulty
	 * drive times out on a command.  See comment on timeout in
	 * INB.
	 */
	if(!timeleft)
		printk("ATA timeout reg 0x%lx := 0x%x\n", reg, data);

	REG_WR_INT(ata, regi_ata, rw_ctrl2, reg | data); /* write data to the drive's register */

	timeleft = IDE_REGISTER_TIMEOUT;
	/* wait for transmitter ready */
	do {
		timeleft--;
		stat_data = REG_RD(ata, regi_ata, rs_stat_data);
	} while(timeleft && stat_data.busy);
}

void
crisv32_ide_outb(unsigned char data, unsigned long reg)
{
	crisv32_ide_outw(data, reg);
}

void
crisv32_ide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port)
{
	crisv32_ide_outw(addr, port);
}

unsigned short
crisv32_ide_inw(unsigned long reg) {
	int timeleft;
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, unsigned long, reg);
	reg_ata_rs_stat_data stat_data;

	timeleft = IDE_REGISTER_TIMEOUT;
	/* wait for busy flag */
	do {
		timeleft--;
		stat_data = REG_RD(ata, regi_ata, rs_stat_data);
	} while(timeleft && stat_data.busy);

	if(!timeleft) {

		/*
		 * If we're asked to read the status register, like for
		 * example when a command does not complete for an
		 * extended time, but the ATA interface is stuck in a
		 * busy state at the *ETRAX* ATA interface level (as has
		 * happened repeatedly with at least one bad disk), then
		 * the best thing to do is to pretend that we read
		 * "busy" in the status register, so the IDE driver will
		 * time-out, abort the ongoing command and perform a
		 * reset sequence.  Note that the subsequent OUT_BYTE
		 * call will also timeout on busy, but as long as the
		 * write is still performed, everything will be fine.
		 */
		if (ctrl2.addr == IDE_STATUS_OFFSET)
			return BUSY_STAT;
		else
			/* For other rare cases we assume 0 is good enough.  */
			return 0;
	}
 

	ctrl2.rw = regk_ata_rd;
	REG_WR(ata, regi_ata, rw_ctrl2, ctrl2);

	timeleft = IDE_REGISTER_TIMEOUT;
	/* wait for available */
	do {
		timeleft--;
		stat_data = REG_RD(ata, regi_ata, rs_stat_data);
	} while(timeleft && !stat_data.dav);

	if(!timeleft)
		return 0;

	LOWDB(printk("inb: 0x%x from reg 0x%x\n", stat_data.data & 0xff, reg));

	return (unsigned short)stat_data.data;
}

unsigned char
crisv32_ide_inb(unsigned long reg)
{
	return (unsigned char)crisv32_ide_inw(reg);
}


#define ATA_UDMA2_CYC    2
#define ATA_UDMA2_DVS    3
#define ATA_UDMA1_CYC    2
#define ATA_UDMA1_DVS    4
#define ATA_UDMA0_CYC    4
#define ATA_UDMA0_DVS    6
#define ATA_DMA2_STROBE  7
#define ATA_DMA2_HOLD    1
#define ATA_DMA1_STROBE  8
#define ATA_DMA1_HOLD    3
#define ATA_DMA0_STROBE 25
#define ATA_DMA0_HOLD   19
#define ATA_PIO4_SETUP   3
#define ATA_PIO4_STROBE  7
#define ATA_PIO4_HOLD    1
#define ATA_PIO3_SETUP   3
#define ATA_PIO3_STROBE  9
#define ATA_PIO3_HOLD    3
#define ATA_PIO2_SETUP   3
#define ATA_PIO2_STROBE 13
#define ATA_PIO2_HOLD    5
#define ATA_PIO1_SETUP   5
#define ATA_PIO1_STROBE 23
#define ATA_PIO1_HOLD    9
#define ATA_PIO0_SETUP   9
#define ATA_PIO0_STROBE 39
#define ATA_PIO0_HOLD    9

static int crisv32_dma_check (ide_drive_t *drive);
static int crisv32_dma_end (ide_drive_t *drive);
static int crisv32_dma_setup (ide_drive_t *drive);
static void crisv32_dma_exec_cmd (ide_drive_t *drive, u8 command);
static int crisv32_dma_test_irq(ide_drive_t *drive);
static void crisv32_dma_start(ide_drive_t *drive);
static void crisv32_ide_input_data (ide_drive_t *drive, void *, unsigned int);
static void crisv32_ide_output_data (ide_drive_t *drive, void *, unsigned int);
static void crisv32_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int);
static void crisv32_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int);
static int crisv32_dma_off (ide_drive_t *drive);
static int crisv32_dma_on (ide_drive_t *drive);

static void tune_crisv32_ide(ide_drive_t *drive, byte pio)
{
	reg_ata_rw_ctrl0 ctrl0 = REG_RD(ata, regi_ata, rw_ctrl0);
	reg_ata_rw_ctrl1 ctrl1 = REG_RD(ata, regi_ata, rw_ctrl1);

	if (pio <= 4)
		pio = ide_get_best_pio_mode(drive, pio, 4, NULL);

	switch(pio)
	{	
		case 0:
			ctrl0.pio_setup = ATA_PIO0_SETUP;
			ctrl0.pio_strb = ATA_PIO0_STROBE;
			ctrl0.pio_hold = ATA_PIO0_HOLD;
			break;
		case 1:
			ctrl0.pio_setup = ATA_PIO1_SETUP;
			ctrl0.pio_strb = ATA_PIO1_STROBE;
			ctrl0.pio_hold = ATA_PIO1_HOLD;
			break;
		case 2:
			ctrl0.pio_setup = ATA_PIO2_SETUP;
			ctrl0.pio_strb = ATA_PIO2_STROBE;
			ctrl0.pio_hold = ATA_PIO2_HOLD;
			break;
		case 3:
			ctrl0.pio_setup = ATA_PIO3_SETUP;
			ctrl0.pio_strb = ATA_PIO3_STROBE;
			ctrl0.pio_hold = ATA_PIO3_HOLD;
			break;
		case 4:
			ctrl0.pio_setup = ATA_PIO4_SETUP;
			ctrl0.pio_strb = ATA_PIO4_STROBE;
			ctrl0.pio_hold = ATA_PIO4_HOLD;
			break;    
	}

	REG_WR(ata, regi_ata, rw_ctrl1, ctrl1);
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);
}

static int speed_crisv32_ide(ide_drive_t *drive, byte speed)
{
	reg_ata_rw_ctrl0 ctrl0 = REG_RD(ata, regi_ata, rw_ctrl0);
	reg_ata_rw_ctrl1 ctrl1 = REG_RD(ata, regi_ata, rw_ctrl1);

	switch(speed)
	{	
		case XFER_UDMA_0:
			ctrl1.udma_tcyc = ATA_UDMA0_CYC;
			ctrl1.udma_tdvs = ATA_UDMA0_DVS;
			break;
		case XFER_UDMA_1:
			ctrl1.udma_tcyc = ATA_UDMA1_CYC;
			ctrl1.udma_tdvs = ATA_UDMA1_DVS;
			break;
		case XFER_UDMA_2:
			ctrl1.udma_tcyc = ATA_UDMA2_CYC;
			ctrl1.udma_tdvs = ATA_UDMA2_DVS;
			break;
		case XFER_MW_DMA_2:
			ctrl0.dma_strb = ATA_DMA0_STROBE;
			ctrl0.dma_hold = ATA_DMA0_HOLD;
			break;
		case XFER_MW_DMA_1:
			ctrl0.dma_strb = ATA_DMA1_STROBE;
			ctrl0.dma_hold = ATA_DMA1_HOLD;
			break;
		case XFER_MW_DMA_0:
			ctrl0.dma_strb = ATA_DMA2_STROBE;
			ctrl0.dma_hold = ATA_DMA2_HOLD;
			break;
		case XFER_PIO_0:
			ctrl0.pio_setup = ATA_PIO0_SETUP;
			ctrl0.pio_strb = ATA_PIO0_STROBE;
			ctrl0.pio_hold = ATA_PIO0_HOLD;
			break;
		case XFER_PIO_1:
			ctrl0.pio_setup = ATA_PIO1_SETUP;
			ctrl0.pio_strb = ATA_PIO1_STROBE;
			ctrl0.pio_hold = ATA_PIO1_HOLD;
			break;
		case XFER_PIO_2:
			ctrl0.pio_setup = ATA_PIO2_SETUP;
			ctrl0.pio_strb = ATA_PIO2_STROBE;
			ctrl0.pio_hold = ATA_PIO2_HOLD;
			break;
		case XFER_PIO_3:
			ctrl0.pio_setup = ATA_PIO3_SETUP;
			ctrl0.pio_strb = ATA_PIO3_STROBE;
			ctrl0.pio_hold = ATA_PIO3_HOLD;
			break;
		case XFER_PIO_4:
			ctrl0.pio_setup = ATA_PIO4_SETUP;
			ctrl0.pio_strb = ATA_PIO4_STROBE;
			ctrl0.pio_hold = ATA_PIO4_HOLD;
			break;
	}

	REG_WR(ata, regi_ata, rw_ctrl1, ctrl1);
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);

	return 0;
}

void __init
init_e100_ide (void)
{
	reg_ata_rw_ctrl0 ctrl0 = {0};
	reg_ata_rw_ctrl1 ctrl1 = {0};
	reg_ata_rw_intr_mask intr_mask = {0};
        hw_regs_t hw;
	int ide_offsets[IDE_NR_PORTS];
	int h;
	int i;
	reg_ata_rw_ctrl2 ctrl2 = {0};

	printk("ide: ETRAX FS built-in ATA DMA controller\n");

	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
		ctrl2.addr = i;
		ctrl2.cs0 = regk_ata_active;
		ide_offsets[i] = REG_TYPE_CONV(int, reg_ata_rw_ctrl2, ctrl2);
	}

	/* the IDE control register is at ATA address 6, with CS1 active instead of CS0 */
	ctrl2.addr = 6;
	ctrl2.cs1 = regk_ata_active;
	ctrl2.cs0 = regk_ata_inactive;
	ide_offsets[IDE_CONTROL_OFFSET] = REG_TYPE_CONV(int, reg_ata_rw_ctrl2, ctrl2);

	memset(&ctrl2, 0, sizeof(ctrl2));

	/* first fill in some stuff in the ide_hwifs fields */

	for(h = 0; h < MAX_HWIFS; h++) {
		reg_ata_rw_ctrl2 ctrl2 = {.sel = h};
		ide_hwif_t *hwif = &ide_hwifs[h];
		ide_setup_ports(&hw, REG_TYPE_CONV(int, reg_ata_rw_ctrl2, ctrl2), 
		                ide_offsets,
		                0, 0, crisv32_ide_ack_intr,
		                ATA_INTR_VECT);
		ide_register_hw(&hw, &hwif);
		hwif->mmio = 2;
		hwif->chipset = ide_etrax100;
		hwif->tuneproc = &tune_crisv32_ide;
		hwif->speedproc = &speed_crisv32_ide;
		hwif->ata_input_data = &crisv32_ide_input_data;
		hwif->ata_output_data = &crisv32_ide_output_data;
		hwif->atapi_input_bytes = &crisv32_atapi_input_bytes;
		hwif->atapi_output_bytes = &crisv32_atapi_output_bytes;
		hwif->ide_dma_check = &crisv32_dma_check;
		hwif->ide_dma_end = &crisv32_dma_end;
		hwif->dma_setup = &crisv32_dma_setup;
		hwif->dma_exec_cmd = &crisv32_dma_exec_cmd;
		hwif->ide_dma_test_irq = &crisv32_dma_test_irq;
		hwif->dma_start = &crisv32_dma_start;
		hwif->OUTB = &crisv32_ide_outb;
		hwif->OUTW = &crisv32_ide_outw;
		hwif->OUTBSYNC = &crisv32_ide_outbsync;
		hwif->INB = &crisv32_ide_inb;
		hwif->INW = &crisv32_ide_inw;
		hwif->ide_dma_host_off = &crisv32_dma_off;
		hwif->ide_dma_host_on = &crisv32_dma_on;
		hwif->ide_dma_off_quietly = &crisv32_dma_off;
		hwif->udma_four = 1;
		hwif->ultra_mask = 0x07; /* UDMA 0-2 */
		hwif->mwdma_mask = 0x07; /* Multiword DMA 0-2 */
		hwif->swdma_mask = 0x07; /* Singleword DMA 0-2 */
		hwif->sg_table =
		  kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, GFP_KERNEL);
	}

	/* actually reset and configure the crisv32 ide/ata interface */
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);

	/* pull the chosen /reset-line low */
	ctrl0.rst = regk_ata_active;
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);

	/* wait some */
	udelay(25);

	/* de-assert bus-reset */
	ctrl0.rst = regk_ata_inactive;
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);

	ctrl0.en = regk_ata_yes;
	ctrl0.dma_strb = ATA_DMA2_STROBE;
	ctrl0.dma_hold = ATA_DMA2_HOLD;
	ctrl0.pio_setup = ATA_PIO4_SETUP;
	ctrl0.pio_strb = ATA_PIO4_STROBE;
	ctrl0.pio_hold = ATA_PIO4_HOLD;
	REG_WR(ata, regi_ata, rw_ctrl0, ctrl0);

	ctrl1.udma_tcyc = ATA_UDMA6_CYC;
	ctrl1.udma_tdvs = ATA_UDMA6_DVS;
	REG_WR(ata, regi_ata, rw_ctrl1, ctrl1);

	intr_mask.bus0 = regk_ata_yes;
	intr_mask.bus1 = regk_ata_yes;
	intr_mask.bus2 = regk_ata_yes;		
	intr_mask.bus3 = regk_ata_yes;

	REG_WR(ata, regi_ata, rw_intr_mask, intr_mask);

	crisv32_request_dma(2, "ETRAX FS built-in ATA", DMA_VERBOSE_ON_ERROR, 0, dma_ata);
	crisv32_request_dma(3, "ETRAX FS built-in ATA", DMA_VERBOSE_ON_ERROR, 0, dma_ata);
        
	crisv32_pinmux_alloc_fixed(pinmux_ata);
	crisv32_pinmux_alloc_fixed(pinmux_ata0);
	crisv32_pinmux_alloc_fixed(pinmux_ata1);
	crisv32_pinmux_alloc_fixed(pinmux_ata2);
	crisv32_pinmux_alloc_fixed(pinmux_ata3);

	DMA_RESET(regi_dma2);
	DMA_ENABLE(regi_dma2);
	DMA_RESET(regi_dma3);
	DMA_ENABLE(regi_dma3);

	DMA_WR_CMD (regi_dma2, regk_dma_set_w_size2);
	DMA_WR_CMD (regi_dma3, regk_dma_set_w_size2);
}

static int crisv32_dma_off (ide_drive_t *drive)
{
	return 0;
}

static int crisv32_dma_on (ide_drive_t *drive)
{
	return 0;
}

static dma_descr_context mycontext __attribute__ ((__aligned__(32)));
static dma_descr_data mydescr __attribute__ ((__aligned__(16)));

/*
 * The following routines are mainly used by the ATAPI drivers.
 *
 * These routines will round up any request for an odd number of bytes,
 * so if an odd bytecount is specified, be sure that there's at least one
 * extra byte allocated for the buffer.
 */
static void
crisv32_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
	reg_ata_rw_trf_cnt trf_cnt = {0};
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, int, IDE_DATA_REG);
	reg_dma_rw_stat status;
	unsigned char* d;

	D(printk("atapi_input_bytes, buffer 0x%x, count %d\n",
	         buffer, bytecount));

	if(bytecount & 1) {
		printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount);
		bytecount++; /* to round off */
	}

	/* setup DMA descriptor */

	mydescr.eol = 1;
	mydescr.buf = (char*)virt_to_phys(buffer);
	mydescr.after = mydescr.buf + bytecount;
	mycontext.saved_data = (dma_descr_data*)virt_to_phys(&mydescr);
	mycontext.saved_data_buf = mydescr.buf;

	/* start the dma channel */
	DMA_START_CONTEXT(regi_dma3, virt_to_phys(&mycontext));

	/* initiate a multi word dma read using PIO handshaking */
	trf_cnt.cnt = bytecount >> 1;
	REG_WR(ata, regi_ata, rw_trf_cnt, trf_cnt);

	d = buffer;
        
	ctrl2.rw = regk_ata_rd;
	ctrl2.trf_mode = regk_ata_dma;
	ctrl2.hsh = regk_ata_pio;
	ctrl2.multi = regk_ata_yes;
	ctrl2.dma_size = regk_ata_word;
	REG_WR(ata, regi_ata, rw_ctrl2, ctrl2);

	/* wait for completion */
	LED_DISK_READ(1);
	do
	{
		status = REG_RD(dma, regi_dma3, rw_stat);
	} while(status.list_state != regk_dma_data_at_eol);
	LED_DISK_READ(0);
}

static void
crisv32_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
	reg_ata_rw_trf_cnt trf_cnt = {0};
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, int, IDE_DATA_REG);
	reg_dma_rw_stat status;

	D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n",
		 0, buffer, bytecount));

	if(bytecount & 1) {
		printk("odd bytecount %d in atapi_out_bytes!\n", bytecount);
		bytecount++;
	}

	/* setup DMA descriptor */

	mydescr.eol = 1;
	mydescr.buf = (char*)virt_to_phys(buffer);
	mydescr.after = mydescr.buf + bytecount;
	mycontext.saved_data = (dma_descr_data*)virt_to_phys(&mydescr);
	mycontext.saved_data_buf = mydescr.buf;

	/* start the dma channel */
	DMA_START_CONTEXT(regi_dma2, virt_to_phys(&mycontext));

	/* initiate a multi word dma write using PIO handshaking */
	trf_cnt.cnt = bytecount >> 1;
	REG_WR(ata, regi_ata, rw_trf_cnt, trf_cnt);	

	ctrl2.rw = regk_ata_wr;
	ctrl2.trf_mode = regk_ata_dma;
	ctrl2.hsh = regk_ata_pio;
	ctrl2.multi = regk_ata_yes;
	ctrl2.dma_size = regk_ata_word;
	REG_WR(ata, regi_ata, rw_ctrl2, ctrl2);

	/* wait for completion */

	LED_DISK_WRITE(1);
	LED_DISK_READ(1);
	do
	{
		status = REG_RD(dma, regi_dma2, rw_stat);
	} while(status.list_state != regk_dma_data_at_eol);
	LED_DISK_WRITE(0);
}

/*
 * This is used for most PIO data transfers *from* the IDE interface
 */
static void
crisv32_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
	crisv32_atapi_input_bytes(drive, buffer, wcount << 2);
}

/*
 * This is used for most PIO data transfers *to* the IDE interface
 */
static void
crisv32_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
	crisv32_atapi_output_bytes(drive, buffer, wcount << 2);
}

/* we only have one DMA channel on the chip for ATA, so we can keep these statically */
static dma_descr_data ata_descrs[MAX_DMA_DESCRS] __attribute__ ((__aligned__(16)));
static unsigned int ata_tot_size;

/*
 * crisv32_ide_build_dmatable() prepares a dma request.
 * Returns 0 if all went okay, returns 1 otherwise.
 */
static int crisv32_ide_build_dmatable (ide_drive_t *drive)
{
	ide_hwif_t *hwif = HWIF(drive);
	struct scatterlist* sg;
	struct request *rq  = HWGROUP(drive)->rq;
	unsigned long size, addr;
	unsigned int count = 0;
	int i = 0;

	sg = hwif->sg_table;

	ata_tot_size = 0;

	if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) {
		u8 *virt_addr = rq->buffer;
		int sector_count = rq->nr_sectors;
		memset(&sg[0], 0, sizeof(*sg));
		sg[0].page = virt_to_page(virt_addr);
		sg[0].offset = offset_in_page(virt_addr);
		sg[0].length =  sector_count  * SECTOR_SIZE;
		hwif->sg_nents = i = 1;
	}
	else
	{
		hwif->sg_nents = i = blk_rq_map_sg(drive->queue, rq, hwif->sg_table);
	}


	while(i) {
		/*
		 * Determine addr and size of next buffer area.  We assume that
		 * individual virtual buffers are always composed linearly in
		 * physical memory.  For example, we assume that any 8kB buffer
		 * is always composed of two adjacent physical 4kB pages rather
		 * than two possibly non-adjacent physical 4kB pages.
		 */
		/* group sequential buffers into one large buffer */
		addr = page_to_phys(sg->page) + sg->offset;
		size = sg_dma_len(sg);
		while (sg++, --i) {
			if ((addr + size) != page_to_phys(sg->page) + sg->offset)
				break;
			size += sg_dma_len(sg);
		}

		/* did we run out of descriptors? */

		if(count >= MAX_DMA_DESCRS) {
			printk("%s: too few DMA descriptors\n", drive->name);
			return 1;
		}

		/* however, this case is more difficult - rw_trf_cnt cannot be more
		   than 65536 words per transfer, so in that case we need to either
		   1) use a DMA interrupt to re-trigger rw_trf_cnt and continue with
		      the descriptors, or
		   2) simply do the request here, and get dma_intr to only ide_end_request on
		      those blocks that were actually set-up for transfer.
		*/

		if(ata_tot_size + size > 131072) {
			printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, (int)size);
			return 1;
		}

		ata_descrs[count].eol = 0;
		ata_descrs[count].buf = (char*)addr;
		ata_descrs[count].after = ata_descrs[count].buf + size;
		ata_descrs[count].next = (dma_descr_data*)virt_to_phys(&ata_descrs[count + 1]);
		count++;
		ata_tot_size += size;
	}

	if (count) {
		/* set the end-of-list flag on the last descriptor */
		ata_descrs[count - 1].eol = 1;
		/* return and say all is ok */
		return 0;
	}

	printk("%s: empty DMA table?\n", drive->name);
	return 1;	/* let the PIO routines handle this weirdness */
}

static int config_drive_for_dma (ide_drive_t *drive)
{
	struct hd_driveid *id = drive->id;

	if (id && (id->capability & 1)) {
		/* Enable DMA on any drive that supports mword2 DMA */
		if ((id->field_valid & 2) && 
		    ((id->dma_mword & 0xff00) || (id->dma_ultra & 0xff00))) {
			drive->using_dma = 1;
			return 0;               /* DMA enabled */
		}
	}
	drive->using_dma = 0;
	return 0;       /* DMA not enabled */
}

/*
 * crisv32_dma_intr() is the handler for disk read/write DMA interrupts
 */
static ide_startstop_t crisv32_dma_intr (ide_drive_t *drive)
{
	int i, dma_stat;
	byte stat;

	LED_DISK_READ(0);
	LED_DISK_WRITE(0);

	dma_stat = HWIF(drive)->ide_dma_end(drive);
	stat = HWIF(drive)->INB(IDE_STATUS_REG);		/* get drive status */
	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
		if (!dma_stat) {
			struct request *rq;
			rq = HWGROUP(drive)->rq;
			for (i = rq->nr_sectors; i > 0;) {
				i -= rq->current_nr_sectors;
				DRIVER(drive)->end_request(drive, 1, rq->nr_sectors);
			}
			return ide_stopped;
		}
		printk("%s: bad DMA status\n", drive->name);
	}
	return ide_error(drive, "dma_intr", stat);
}

/*
 * Functions below initiates/aborts DMA read/write operations on a drive.
 *
 * The caller is assumed to have selected the drive and programmed the drive's
 * sector address using CHS or LBA.  All that remains is to prepare for DMA
 * and then issue the actual read/write DMA/PIO command to the drive.
 *
 * For ATAPI devices, we just prepare for DMA and return. The caller should
 * then issue the packet command to the drive and call us again with
 * ide_dma_begin afterwards.
 *
 * Returns 0 if all went well.
 * Returns 1 if DMA read/write could not be started, in which case
 * the caller should revert to PIO for the current request.
 */

static int crisv32_dma_check(ide_drive_t *drive)
{
	int speed = ide_dma_speed(drive, 0); /* No UDMA for now */
	speed_crisv32_ide(drive, speed);
	ide_config_drive_speed(drive, speed);
	return config_drive_for_dma (drive);
}

static int crisv32_dma_end(ide_drive_t *drive)
{
	drive->waiting_for_dma = 0;
	return 0;
}

static int crisv32_prepare_dma(ide_drive_t *drive, int atapi, int reading)
{
	struct request *rq = drive->hwif->hwgroup->rq;
	if (crisv32_ide_build_dmatable (drive)) {
		ide_map_sg(drive, rq);
		return 1;
	}

	return 0;
}

static int crisv32_dma_setup(ide_drive_t *drive)
{
	struct request *rq = HWGROUP(drive)->rq;
	int ret;
	if (rq_data_dir(rq))
		ret = crisv32_prepare_dma(drive, 0, 0);
        else
		ret = crisv32_prepare_dma(drive, 0, 1);
	if (ret)
		return ret;
	drive->waiting_for_dma = 1;
	return 0;
}

static void crisv32_dma_exec_cmd(ide_drive_t *drive, u8 command)
{
	/* set the irq handler which will finish the request when DMA is done */
	ide_set_handler(drive, &crisv32_dma_intr, WAIT_CMD, NULL);

	/* issue cmd to drive */
	crisv32_ide_outb(command, IDE_COMMAND_REG);
}

static int crisv32_dma_test_irq(ide_drive_t *drive)
{
	int intr = REG_RD_INT(ata, regi_ata, r_intr);
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, int, IDE_DATA_REG);
	return intr & (1 << ctrl2.sel) ? 1 : 0;
}


static void crisv32_dma_start(ide_drive_t *drive)
{
	struct request *rq = HWGROUP(drive)->rq;
	reg_ata_rw_trf_cnt trf_cnt = {0};
	int writing = rq_data_dir(rq);
	reg_ata_rw_ctrl2 ctrl2 = REG_TYPE_CONV(reg_ata_rw_ctrl2, int, IDE_DATA_REG);

	/* initiate a multi word dma read using DMA handshaking */
	mycontext.saved_data = (dma_descr_data*)virt_to_phys(&ata_descrs[0]);
	mycontext.saved_data_buf = ata_descrs[0].buf;

	if (writing)
		DMA_START_CONTEXT(regi_dma2, virt_to_phys(&mycontext));
	else
		DMA_START_CONTEXT(regi_dma3, virt_to_phys(&mycontext));

	trf_cnt.cnt = ata_tot_size >> 1;
	ctrl2.rw = writing ? regk_ata_wr : regk_ata_rd;
	ctrl2.trf_mode = regk_ata_dma;
	if (drive->current_speed >= XFER_UDMA_0) {
		/* Due to a "feature" the transfer count has to be one extra word for UDMA. */
		trf_cnt.cnt++;
		ctrl2.hsh = regk_ata_udma;
	}
	else
		ctrl2.hsh = regk_ata_dma;
	ctrl2.multi = regk_ata_yes;
	ctrl2.dma_size = regk_ata_word;
	REG_WR(ata, regi_ata, rw_trf_cnt, trf_cnt);
	REG_WR(ata, regi_ata, rw_ctrl2, ctrl2);

	if (writing) {
		LED_DISK_WRITE(1);
	} else {
		LED_DISK_READ(1);
	}
}


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2005-06-27 15:36 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <BFECAF9E178F144FAEF2BF4CE739C668030C7157@exmail1.se.axis.com>
2005-06-27 15:17 ` New IDE driver for review Mikael Starvik
2005-06-27 15:36   ` Bartlomiej Zolnierkiewicz
     [not found] <BFECAF9E178F144FAEF2BF4CE739C668030176E7@exmail1.se.axis.com>
2005-06-17 14:16 ` Mikael Starvik
2005-06-20 19:15   ` Bartlomiej Zolnierkiewicz
     [not found] <BFECAF9E178F144FAEF2BF4CE739C668030174FD@exmail1.se.axis.com>
2005-06-09 10:29 ` Mikael Starvik
2005-06-09 12:54 ` Mikael Starvik
2005-06-09 15:10   ` Bartlomiej Zolnierkiewicz
2005-06-09  7:18 Mikael Starvik
2005-06-09 10:14 ` Bartlomiej Zolnierkiewicz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).