/*
 * MediaSwitch IDE chipset-specific code
 *
 * Supports both PIO and DMA transfers, single channel (2 drives)
 *
 * Copyright (C) 2001-2003 TiVo Inc. All Rights Reserved.
 */

#include <linux/ide.h>
#include <linux/delay.h>
#include <linux/tivoconfig.h>
#include <linux/module.h>
#include <linux/plog.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/px_hw.h>

#undef DENY_ALL_DMA_REQUESTS
#define SUPPORT_SG

#ifdef CONFIG_TIVO_DEVEL
#define DEBUG_IDE_REGS
//#define DEBUG_IDE_REGS_WITH_GPIO11
#endif

static int fpga_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);

enum fpga_dma_state {
	FPGA_DMA_IDLE,		// no DMA transfer in progress
	FPGA_DMA_READ_WAIT,	// waiting for read drive int
	FPGA_DMA_READ_WAIT2,	// waiting for read DMA int
	FPGA_DMA_WRITE_WAIT,	// waiting for write DMA completion
	FPGA_DMA_DONE,		// received DMA interrupt
	FPGA_DMA_ERROR,		// DMA operation timed out
};

static enum fpga_dma_state gDMAState = FPGA_DMA_IDLE;
static int fDMAError = 0;
static u32* ms_ide_data;
static void (*real_intr_handler)(int,void *, struct pt_regs *);
static void (*fpga_cmd_hook)(ide_drive_t *, unsigned char *);

struct fpga_sgent {
	u32	address;
	u32	count;
};

struct desc_chunk {
	unsigned int ba_sglist;	// bus address of scatter list
	unsigned int count;		// count value for IDELL DMA
};

struct fpga_ide_priv {
	struct desc_chunk *first_chunk;
	struct desc_chunk *cur_chunk;
	struct fpga_sgent *first_sgent;
	struct fpga_sgent *cur_sgent;
	int big_flush;
        int last_size;
        u32 bus_addr_end;
	u32 count_ctl;
};

#ifdef CONFIG_TIVO_DEVEL // debug build

static int maxudma=4;

static int __init maxudma_setup(char *str)
{
	get_option(&str, &maxudma);
	return 1;
}

__setup("maxudma=", maxudma_setup);

#else // release build
static const int maxudma=4;
#endif

#define SG_ENTRIES (PAGE_SIZE / sizeof(struct fpga_sgent))

#define DMA_DONE_BIT (1 << (IRQ_MS_IDEDI - MEDIASWITCH_IBASE))
#define DRIVE_DONE_BIT (1 << (IRQ_MS_IDE - MEDIASWITCH_IBASE))

#ifdef SUPPORT_SG

//
// These functions are used to preprocess TiVo I/O requests
//
// Report back how much room needed to allocate along with the request
// for use by the DMA engine, based on number of scatter list entries,
// and number of distinct scatter lists.  Each separate IDE transfer
// (of max MAX_NRSECTORS sectors) requires a separate scatter list.
//
static unsigned int fpga_size_priv(int dma_chunks, int block_chunks)
{
	return sizeof(struct fpga_ide_priv) +
	       block_chunks * sizeof(struct desc_chunk) +
	       L1_CACHE_BYTES - 4 +  // worst case cache line alignment
	       dma_chunks * sizeof(struct fpga_sgent);
}

// put the chunk list right after the priv struct
// put the scatter list after the chunk list (but cache line align it)
//
static void fpga_init_drvpriv(struct idetivo_request *trq, int reading)
{
    struct fpga_ide_priv *priv = trq->drv_priv;
    unsigned long tmp;

    priv->first_chunk = (struct desc_chunk *)((unsigned long)priv +
                                              sizeof(struct fpga_ide_priv));

    tmp = (unsigned long)(priv->first_chunk + trq->num_chunks);
    priv->first_sgent = (struct fpga_sgent *)L1_CACHE_ALIGN(tmp);

    priv->cur_sgent = priv->first_sgent;
    priv->cur_chunk = priv->first_chunk;
    priv->cur_chunk->ba_sglist = virt_to_bus(priv->cur_sgent);
    priv->cur_chunk->count = 0x01000000;

    // this is a bit of platform specificness, but there's no generic way
    // to do it: if the request totals more than twice the dcache size,
    // mark to do one big flush, instead of flushing each individual chunk
    //
    priv->big_flush = (trq->req->nr_sectors > 64 * 1024 / SECTOR_BYTES);

    priv->bus_addr_end = 1; // an impossible addr

    if (reading) {
        priv->count_ctl = cpu_to_le32(0x01000000);
    } else {
        priv->count_ctl = cpu_to_le32(0x11000000);
    }
}

// Add a chunk to the DMA scatter list and do any necessary cache flushing
// Both kernel and user virtual addresses are passed in, in case the user
// address is needed for flushing purposes (to avoid virtual aliasing
// problem on CPUs with virually indexed caches).  The user address is
// guaranteed not to page fault and size is guaranteed to be non-zero.
//
static int fpga_add_dmachunk(struct idetivo_request *trq, unsigned long k_addr,
                             unsigned long u_addr, int size, int reading)
{
    struct fpga_ide_priv *priv = trq->drv_priv;
    struct fpga_sgent *sgent = priv->cur_sgent;
    u32 bus_addr;
    int total_size;

    if ((k_addr | size) & 3) {
        printk(KERN_WARNING "fpga_ide: DMA not word aligned\n");
        return -EINVAL;
    }

    if (!priv->big_flush) {
        if (reading) {
            dma_cache_inv(u_addr, size);
        } else {
            dma_cache_wback_inv(u_addr, size);
        }
    }

    bus_addr = (u32)virt_to_bus((void *)k_addr);

    // Consolidate adjacent chunks
    //
    // Note that total size will never exceed 128K, because we start a
    // new scatter list at least every 128K
    //
    if (bus_addr == priv->bus_addr_end) {
        sgent--;
        total_size = size + priv->last_size;
    } else {
        priv->cur_chunk->count += sizeof(struct fpga_sgent);
        sgent->address = cpu_to_le32(bus_addr);
        total_size = size;
    }
    sgent->count = cpu_to_le32((u32)total_size) | priv->count_ctl;

    priv->last_size = total_size;
    priv->bus_addr_end = bus_addr + size;

    priv->cur_sgent = sgent + 1;
    return 0;
}

// Finish up a block of DMA chunks that makes up a single IDE transfer,
// and start a new one
//
static void fpga_add_blkchunk(struct idetivo_request *trq, int chunks, int sectors)
{
    struct fpga_ide_priv *priv = trq->drv_priv;
    struct desc_chunk *desc = priv->cur_chunk++;

    (priv->cur_sgent - 1)->count |= cpu_to_le32(0x40000000);
    (desc + 1)->ba_sglist = virt_to_bus(priv->cur_sgent);
    (desc + 1)->count = 0x01000000;

    priv->bus_addr_end = 1; // an impossible addr
}

// Finish up the last block of DMA chunks, and do any final prep on the
// data going to the DMA engine
//
static void fpga_end_blkchunk(struct idetivo_request *trq, int chunks, int sectors)
{
    struct fpga_ide_priv *priv = trq->drv_priv;
    unsigned long sg_start = (unsigned long)priv->first_sgent;
    unsigned long sg_end = (unsigned long)priv->cur_sgent;

    (priv->cur_sgent - 1)->count |= cpu_to_le32(0x40000000);
    if (!priv->big_flush) {
        dma_cache_wback_inv(sg_start, sg_end - sg_start);
    } else {
        flush_dcache();
    }
    priv->cur_chunk = priv->first_chunk;
}

static void fpga_set_cmd_hook(void (*handler)(ide_drive_t *, unsigned char *)) {
    fpga_cmd_hook = handler;
}

static struct ll_dma_ops fpga_sgops = {
    size_priv:      fpga_size_priv,
    init_drvpriv:   fpga_init_drvpriv,
    add_dmachunk:   fpga_add_dmachunk,
    add_blkchunk:   fpga_add_blkchunk,
    end_blkchunk:   fpga_end_blkchunk,
    set_cmd_hook:   fpga_set_cmd_hook,
};

static inline int fpga_build_dmatable(ide_drive_t *drive)
{
	struct request *rq = HWGROUP(drive)->rq;
	u32 count_ctl, ll_count_ctl;

#ifdef DENY_ALL_DMA_REQUESTS
	return 1;
#endif
	MsAndReg(MS_CONTROL_REGISTER,~0x04000000);
	MsOrReg(MS_SYSTEM_REGISTER,0x00000002);
	MsWrite(MS_IDE_SCRAMBLER_0,0);
	MsWrite(MS_IDE_SCRAMBLER_1,0);
	MsWrite(MS_IDE_SCRAMBLER_2,0);
	MsWrite(MS_IDE_SCRAMBLER_3,0);

	if (rq->tivo) { /* TiVo request, mostly preprocessed */
		struct fpga_ide_priv *priv = rq->tivo->drv_priv;
		struct desc_chunk *chunk = priv->cur_chunk++;
		struct io_scramble *pscramble = &rq->tivo->scramble;
		rq->tivo->status = IDETIVO_INPROGRESS;

		if (pscramble->magic == IO_SCRAMBLE_MAGIC &&
		    pscramble->version == IO_SCRAMBLE_VERSION_TIVO1) {
			struct scrambleConfig config;

                        prepare_scramble_regs(&config, pscramble);

			MsAndReg(MS_SYSTEM_REGISTER,~0x00000002);
			MsWrite(MS_IDE_SCRAMBLER_0,config.wyrd[0]);
			MsWrite(MS_IDE_SCRAMBLER_1,config.wyrd[1]);
			MsWrite(MS_IDE_SCRAMBLER_2,config.wyrd[2]);
			MsWrite(MS_IDE_SCRAMBLER_3,config.wyrd[3]);
			MsOrReg(MS_CONTROL_REGISTER,0x04000000);
		}

		ll_count_ctl = chunk->count;
		MsWrite(MS_DMA_CTL(CH_IDELL,REG_NEXT_ADDR), chunk->ba_sglist);
	} else { /* normal Linux request */
		struct fpga_sgent *sg = (struct fpga_sgent *)HWIF(drive)->sg_table;
		int ent = 0;
		struct buffer_head *bh;
		unsigned long virt_addr, dram_addr;
		unsigned int size;

		if (rq->cmd == READ) {
			count_ctl = cpu_to_le32(0x01000000);
		} else {
			count_ctl = cpu_to_le32(0x11000000);
		}

		bh = rq->bh;
		do {
			virt_addr = (unsigned long)bh->b_data;
			size = bh->b_size;
			while ((bh = bh->b_reqnext) != NULL) {
				if ((virt_addr + size) != (unsigned long) bh->b_data)
					break;
				size += bh->b_size;
			}
			dram_addr = virt_to_bus((void *)virt_addr);
			// flush each chunk of data out to DRAM
			if (rq->cmd == READ) {
				dma_cache_inv(virt_addr, size);
			} else {
				dma_cache_wback_inv(virt_addr, size);
			}
			if (size & 3 || virt_addr & 3) {
				printk(KERN_WARNING "fpga_ide: DMA not word aligned\n");
				return 1;
			}
			do {
				if (ent > SG_ENTRIES){
					printk(KERN_WARNING "fpga_ide: sg table overflow\n");
					return 1;
				}
				sg[ent].address = cpu_to_le32((u32)dram_addr);
				sg[ent].count = count_ctl | cpu_to_le32(size);
				size = 0;
				ent++;
			} while (size);
		} while (bh != NULL);

		sg[ent - 1].count |= cpu_to_le32(0x40000000);
		ll_count_ctl = 0x01000000 | (ent << 3);
		MsWrite(MS_DMA_CTL(CH_IDELL,REG_NEXT_ADDR), virt_to_bus(sg));

		// flush the descriptor table out to DRAM
		dma_cache_wback_inv((unsigned long)sg, ent * sizeof(struct fpga_sgent));
		barrier();
	} /* end if TiVo request */

	// reset the IDE FIFO, just in case...
	MsOrReg(MS_SYSTEM_REGISTER, 0x00000100);
	MsAndReg(MS_SYSTEM_REGISTER, ~0x00000100);

	if (rq->cmd == READ) {
		count_ctl = 0x00000000;
		gDMAState = FPGA_DMA_READ_WAIT;
	} else {
		count_ctl = 0x10000000;
		gDMAState = FPGA_DMA_WRITE_WAIT;
	}
	// prime the DMA FIFO with correct read/write direction:
	MsWrite(MS_DMA_CTL(CH_IDE,REG_NEXT_COUNT), count_ctl);
	// and fire off the link-list xfer:
	MsWrite(MS_DMA_CTL(CH_IDELL,REG_NEXT_COUNT), ll_count_ctl);

	return 0;
}

#else // ! SUPPORT_SG

// NOTE: this is mostly reference code, and is not being maintained.
// In particular, the TiVo request extensions are not processed by
// this code.

static inline int fpga_build_dmatable(ide_drive_t *drive)
{
	struct request *rq = HWGROUP(drive)->rq;
	struct buffer_head *bh;
	unsigned long virt_addr, bus_addr;
	unsigned int size;

#ifdef DENY_ALL_DMA_REQUESTS
	return 1;
#endif
	bh = rq->bh;
	virt_addr = (unsigned long)bh->b_data;
	size = bh->b_size;
	while ((bh = bh->b_reqnext) != NULL) {
		if ((virt_addr + size) != (unsigned long) bh->b_data)
			break;
		size += bh->b_size;
	}
	if (bh != NULL) {
		return 1; // can only handle 1 contig block
	}

	bus_addr = virt_to_bus((void *)virt_addr);
	if ((long)bus_addr & 0x03)
		return 1; // must be word aligned

	// reset the DMA FIFO, just in case...
	MsWrite(MS_SYSTEM_REGISTER, 0x100);
	MsWrite(MS_SYSTEM_REGISTER, 0x000);

	MsWrite(MS_DMA_CTL(CH_IDE,REG_NEXT_ADDR), bus_addr);
	if (rq->cmd == READ) {
		size |= 0x41000000;
		dma_cache_inv(virt_addr, size);
		gDMAState = FPGA_DMA_READ_WAIT;
	} else {
		size |= 0x51000000;
		dma_cache_wback_inv(virt_addr, size);
		gDMAState = FPGA_DMA_WRITE_WAIT;
	}
	barrier();
	MsWrite(MS_DMA_CTL(CH_IDE,REG_NEXT_COUNT), size);

	return 0;
}

#endif // SUPPORT_SG

// For DMA reads, the end of the transaction is signaled when the DMA
// xfer is done, not when the drive is done, which will happen first.
//
// For DMA writes, the end of the transaction is signaled when the drive
// is done, which happens after the DMA xfer completes.
//
// The Linux IDE driver only wants one IRQ entry point, but we take 2
// interrupts each time, so we fake it out like this.  This is a little
// messy, but it works...:
//
static void dma_done_intr(int irq, void *dev_id, struct pt_regs *regs)
{
	int do_int;

	// send on the interrupt if we were waiting for read completion
	// and have already seen the drive interrupt
	do_int = (gDMAState == FPGA_DMA_READ_WAIT2);

	// or if the drive interrupt fires simultaneously
	//
	// I really hate this, because it goes behind the back of the
	// core kernel's interrupt management code, but it deals with
	// the common case of getting both ints simultaneously without
	// having to take a separate interrupt, also, it's the only
	// simple way of dealing with this case anyway, without making
	// a lot of assumptions about the IDE layer just above this one
	//
	// It's also almost certainly not SMP safe...
	//
	if (MsRead(MS_ISR_REG) & DRIVE_DONE_BIT) {
		MsWrite(MS_ISR_REG, DRIVE_DONE_BIT);
		do_int = 1;
	}

	gDMAState = FPGA_DMA_DONE;
	if (do_int) {
		real_intr_handler(irq, dev_id, regs);
	}
}

static void drive_done_intr(int irq, void *dev_id, struct pt_regs *regs)
{
	// See note above about "I really hate this..."
	if (MsRead(MS_ISR_REG) & DMA_DONE_BIT) {
		gDMAState = FPGA_DMA_DONE;
		MsWrite(MS_ISR_REG, DMA_DONE_BIT);
	}

	// ignore the drive interrupt if we're still waiting on a DMA
	// completion for a read operation, unless the drive is reporting
	// an error
	if (gDMAState == FPGA_DMA_READ_WAIT) {
		ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
		ide_drive_t *drive = hwgroup->drive;
		byte stat;

		stat = GET_STAT();
		if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
			gDMAState = FPGA_DMA_READ_WAIT2;
			return;
		}
	}

	real_intr_handler(irq, dev_id, regs);
}

static void dma_err_intr(int irq, void *dev_id, struct pt_regs *regs)
{
	fDMAError = irq;
	real_intr_handler(irq, dev_id, regs);
}

static ide_startstop_t fpga_dma_intr(ide_drive_t *drive)
{
	int i, dma_stat;
	byte stat;
	struct request *rq = HWGROUP(drive)->rq;
	struct idetivo_request *trq = rq->tivo;

	dma_stat = fpga_dmaproc(ide_dma_end, drive);
	stat = GET_STAT();			/* get drive status */
	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
		if (!dma_stat) {
			plog_diskstop(rq);
			if (trq) {
				unsigned int max_nrsectors = trq->max_nrsectors;

				if (rq->nr_sectors > max_nrsectors) {
					ide_hwif_t *hwif = HWIF(drive);
					ide_startstop_t startstop;

					rq->nr_sectors -= max_nrsectors;
					rq->sector += max_nrsectors;
					trq->progress += max_nrsectors * SECTOR_BYTES;
					add_blkdev_randomness(MAJOR(rq->rq_dev));
					SELECT_DRIVE(hwif, drive);
					if (ide_wait_stat(&startstop, drive, drive->ready_stat,
					                  BUSY_STAT|DRQ_STAT, WAIT_READY)) {
						printk("%s: drive not ready for command\n", drive->name);
						return startstop;
					}
					trq->scramble.recordOffset++;
					return DRIVER(drive)->do_request(drive, rq, rq->sector
					                                  + trq->partition_start);
				} else {
					trq->progress += rq->nr_sectors * SECTOR_BYTES;
					ide_end_request(1, HWGROUP(drive));
					trq->status = IDETIVO_DONE;
					return ide_stopped;
				}
			} else {
				for (i = rq->nr_sectors; i > 0;) {
					i -= rq->current_nr_sectors;
					ide_end_request(1, HWGROUP(drive));
				}
			}
			return ide_stopped;
		}
		printk("%s: dma_intr: bad DMA status\n", drive->name);
	}
	if (trq) {
		struct fpga_ide_priv *priv = trq->drv_priv;

		// if the request gets retried, we need to reprocess this
		// chunk of the DMA scatterlist
		priv->cur_chunk--;
		trq->status = IDETIVO_ERROR;
		trq->drive_status = stat;
		// XXX: should probably read drive regs to query how far we
		// got in last 256 sector (max) chunk, too...
	}
#ifdef DEBUG_IDE_REGS_WITH_GPIO11
	MsOrReg(MS_GPIOOB_REG, 0x0800);
	udelay(1);
	MsAndReg(MS_GPIOOB_REG, ~0x0800);
#endif
	return ide_error(drive, "dma_intr", stat);
}

/* Teardown mappings after DMA has completed.  */
static inline void fpga_destroy_dmatable(ide_drive_t *drive)
{
// nothing to do
}

static inline int config_drive_for_dma(ide_drive_t *drive)
{
	struct hd_driveid *id = drive->id;
	u32 udma_control, udma_timing, dma_timing, udma_ctlmask;
	byte speed;

	int udma_66 = id->hw_config & 0x2000;
	TivoConfigValue ide_cap;

	// This can change at run time, so reread the config value
	if (GetTivoConfig(kTivoConfigIdeCap, &ide_cap) ||
	    ((ide_cap & kTivoConfigIdeCapAta66) == 0)) {
		udma_66 = 0;
	}

	udma_control = 0;
	udma_timing = 0;
	dma_timing = 0;

	if ((id->dma_ultra & 0x0010) && udma_66 && maxudma >= 4) {
		speed = XFER_UDMA_4;
		udma_control = 1;
		udma_timing = 0x0702;	// TBD
	} else if ((id->dma_ultra & 0x0008) && udma_66 && maxudma >= 3) {
		speed = XFER_UDMA_3;
		udma_control = 1;
		udma_timing = 0x0703;
	} else if (id->dma_ultra & 0x0004 && maxudma >= 2) {
		speed = XFER_UDMA_2;
		udma_control = 1;
		udma_timing = 0x0704;
	} else if (id->dma_ultra & 0x0002 && maxudma >= 1) {
		speed = XFER_UDMA_1;
		udma_control = 1;
		udma_timing = 0x0705;
	} else if (id->dma_ultra & 0x0001 && maxudma >= 0) {
		speed = XFER_UDMA_0;
		udma_control = 1;
		udma_timing = 0x0708;
	} else if (id->dma_mword & 0x0004) {
		speed = XFER_MW_DMA_2;
		dma_timing = 0x0205;
	} else if (id->dma_mword & 0x0002) {
		speed = XFER_MW_DMA_1;
		dma_timing = 0;		// TBD
	} else if (id->dma_mword & 0x0001) {
		speed = XFER_MW_DMA_0;
		dma_timing = 0;		// TBD
	} else if (id->dma_1word & 0x0004) {
		speed = XFER_SW_DMA_2;
		dma_timing = 0;		// MFK: does this even work?
	} else if (id->dma_1word & 0x0002) {
		speed = XFER_SW_DMA_1;
		dma_timing = 0;		// MFK: does this even work?
	} else if (id->dma_1word & 0x0001) {
		speed = XFER_SW_DMA_0;
		dma_timing = 0;		// MFK: does this even work?
        } else {
		return fpga_dmaproc(ide_dma_off_quietly, drive);
	}

	if (drive->select.b.unit & 0x01) {
		MsWrite(MS_IDE1_ULTRA_DMA_TIMING_REG, udma_timing);
		MsWrite(MS_IDE1_MWDMA_TIMING_REG, dma_timing);
		udma_control <<= 1;
		udma_ctlmask = 1;
	} else {
		MsWrite(MS_IDE0_ULTRA_DMA_TIMING_REG, udma_timing);
		MsWrite(MS_IDE0_MWDMA_TIMING_REG, dma_timing);
		udma_ctlmask = 2;
	}
	udma_control |= MsRead(MS_IDE_ULTRA_DMA_CONTROL_REG) & udma_ctlmask;
	MsWrite(MS_IDE_ULTRA_DMA_CONTROL_REG, udma_control);
	if (!drive->init_speed)
		drive->init_speed = speed;
	ide_config_drive_speed(drive, speed);
	drive->current_speed = speed;

	return fpga_dmaproc(ide_dma_on, drive);
}

#ifdef DEBUG_IDE_REGS
static void fpga_dma_error(const char* errmsg)
{
	unsigned int idenc, idena, idecc, ideca;
	unsigned int illnc, illna, illcc, illca;
	unsigned int heis;

	idenc = MsRead(MS_DMA_CTL(CH_IDE,REG_NEXT_COUNT));
	idena = MsRead(MS_DMA_CTL(CH_IDE,REG_NEXT_ADDR));
	idecc = MsRead(MS_DMA_CTL(CH_IDE,REG_CUR_COUNT));
	ideca = MsRead(MS_DMA_CTL(CH_IDE,REG_CUR_ADDR));
	illnc = MsRead(MS_DMA_CTL(CH_IDELL,REG_NEXT_COUNT));
	illna = MsRead(MS_DMA_CTL(CH_IDELL,REG_NEXT_ADDR));
	illcc = MsRead(MS_DMA_CTL(CH_IDELL,REG_CUR_COUNT));
	illca = MsRead(MS_DMA_CTL(CH_IDELL,REG_CUR_ADDR));
	heis = MsRead(MS_HEIS_REG);

#ifdef DEBUG_IDE_REGS_WITH_GPIO11
	MsOrReg(MS_GPIOOB_REG, 0x0800);
	udelay(1);
	MsAndReg(MS_GPIOOB_REG, ~0x0800);
#endif

	printk(KERN_ERR "fpga_ide: %s\n"
	       "  IDEDMA: NC=%08x NA=%08x CC=%08x CA=%08x\n"
	       "  IDE_LL: NC=%08x NA=%08x CC=%08x CA=%08x\n"
	       "  HEIS: %08x\n", errmsg,
	       idenc, idena, idecc, ideca, illnc, illna, illcc, illca, heis);

	if (fDMAError == IRQ_MSE_DFE0) {
		printk(KERN_ERR "  DMA Channel 0 error\n");
	} else if (fDMAError == IRQ_MSE_DFE1) {
		printk(KERN_ERR "  DMA Channel 0 error\n");
	}
}
#else
static inline void fpga_dma_error(const char* errmsg)
{
        printk(KERN_INFO "fpga_ide: %s\n", errmsg);
}
#endif

static void stop_dma(void)
{
#ifdef SUPPORT_SG
	MsWrite(MS_DMA_CTL(CH_IDELL,REG_NEXT_COUNT), 0);
	MsWrite(MS_DMA_CTL(CH_IDELL,REG_CUR_COUNT), 0);
#endif
	MsWrite(MS_DMA_CTL(CH_IDE,REG_NEXT_COUNT), 0);
	MsWrite(MS_DMA_CTL(CH_IDE,REG_CUR_COUNT), 0);

	// in some (rare) cases of drive error, it's possible DMA done int
	// still pending
	MsWrite(MS_ISR_REG, DMA_DONE_BIT);

	gDMAState = FPGA_DMA_IDLE;
}

/*
 * ide_dmaproc() 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 fpga_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
{
	int writing = 0;
	unsigned int stat;
	unsigned char cmdcode;
	enum fpga_dma_state oldDMAState;

	switch (func) {
		case ide_dma_off:
			printk("%s: DMA disabled\n", drive->name);
		case ide_dma_off_quietly:
			stop_dma();
			drive->using_dma = 0;
			return 0;
		case ide_dma_on:
			drive->using_dma = 1;
			return 0;
		case ide_dma_check:
			return config_drive_for_dma(drive);
		case ide_dma_write:
			writing = 1;
		case ide_dma_read:
			if (fpga_build_dmatable(drive))
				return 1;	/* try PIO instead of DMA */
			drive->waiting_for_dma = 1;
			if (drive->media != ide_disk)
				return 0;
			ide_set_handler(drive, &fpga_dma_intr, WAIT_CMD, NULL);
			if (drive->addressing) {
				cmdcode = writing ? WIN_WRITEDMA_EXT : WIN_READDMA_EXT;
			} else {
				cmdcode = writing ? WIN_WRITEDMA : WIN_READDMA;
			}
			if (fpga_cmd_hook)
				fpga_cmd_hook(drive, &cmdcode);
			plog_diskstart(HWGROUP(drive)->rq);
			OUT_BYTE(cmdcode, IDE_COMMAND_REG);
		case ide_dma_begin:
			return 0;
		case ide_dma_end: /* returns 1 on error, 0 otherwise */
			oldDMAState = gDMAState;
			gDMAState = FPGA_DMA_IDLE;
			stat =
#ifdef SUPPORT_SG
			       MsRead(MS_DMA_CTL(CH_IDELL,REG_CUR_COUNT)) |
			       MsRead(MS_DMA_CTL(CH_IDELL,REG_NEXT_COUNT)) |
#endif
			       MsRead(MS_DMA_CTL(CH_IDE,REG_CUR_COUNT)) |
			       MsRead(MS_DMA_CTL(CH_IDE,REG_NEXT_COUNT)) ;
			if (stat & (1 << 24)) {
				fpga_dma_error("DMA not done");
				stop_dma();
			} else if (stat & (1 << 27)) {
				fpga_dma_error("DMA error status");
			}
			drive->waiting_for_dma = 0;
			fpga_destroy_dmatable(drive);
			if (oldDMAState == FPGA_DMA_ERROR) {
				// Error already logged in ide_dma_lostirq
				return 1;
			}
			if (fDMAError) {
				fpga_dma_error("error int asserted");
				fDMAError = 0;
				return 1;
			}
			return (stat & ((1 << 24) | (1 << 27))) != 0;
		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
			if ((gDMAState == FPGA_DMA_DONE) || fDMAError) {
				return 1;
			} else if (gDMAState == FPGA_DMA_IDLE) {
				// this should never happen
				fpga_dma_error("DMA interrupt at IDLE state?");
				return 0;
			} else {
				// this will happen if the drive reports an
				// error before DMA channel completes
				stop_dma();
				return 1;
			}
		case ide_dma_lostirq:
			stop_dma();
			fpga_dma_error("lost IRQ");
			// Make sure this transaction fails, even if the drive
			// wasn't aware of any error condition.
			gDMAState = FPGA_DMA_ERROR;
			return 1;
		case ide_dma_verbose:
                        // OK, so maybe not so verbose, but better than spewing
                        // the log message to console, since this is called
                        // with an unterminated (by '\n') printk
			return 1;
		case ide_dma_bad_drive:
		case ide_dma_good_drive:
		case ide_dma_timeout:
		default:
			printk(KERN_DEBUG "fpga_ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func);
			return 1;
	}
}

static void fpga_resetproc(ide_drive_t *drive)
{
	ide_hwif_t *hwif = HWIF(drive);
	unsigned int unit;

	stop_dma();

	// in some cases, the ide reset code will turn off DMA for a drive
	// other than the one that caused the reset, so recheck all drives
	// on this hwif
	for (unit = 0; unit < MAX_DRIVES; ++unit) {
		ide_drive_t *check_drv = &hwif->drives[unit];

		if (check_drv->present && !check_drv->using_dma) {
			fpga_dmaproc(ide_dma_check, check_drv);
			printk("%s: re-enabled DMA\n", check_drv->name);
		}
	}
}

static int fpga_ide_default_irq(ide_ioreg_t base)
{
	return IRQ_MS_IDE;
}

// Rather than the actual IO base (since FPGA only suports 1 IDE interface),
// this routine just returns 1 to indicate the interface is valid, or 0 to
// indicate it is not.  That value gets passed to init_hwif_ports, below.
static ide_ioreg_t fpga_ide_default_io_base(int index)
{
	if (index)
		return 0;
	else
		return 1;
}

static void fpga_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
                         ide_ioreg_t ctrl_port, int *irq)
{
	int i;
	void *msbase;
	TivoConfigValue ide_cap;

	if (!data_port)
		return;

	if (!(msbase = MsGetBase()))
		return;
	ms_ide_data = (u32 *)((unsigned long)msbase + MS_IDE_DATA_REG);

	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++)
		hw->io_ports[i] = MS_IDE_DATA_REG + i * 4;
	hw->io_ports[IDE_CONTROL_OFFSET] = MS_IDE_DEV_CONTROL_REG;
	if (irq != NULL)
		*irq = IRQ_MS_IDE;
	hw->io_ports[IDE_IRQ_OFFSET] = IRQ_MS_IDE;

	MsOrReg(MS_SYSTEM_REGISTER, 0x102);
	udelay(10);
	MsAndReg(MS_SYSTEM_REGISTER, ~0x10a);

	// Set the PIO timing for PIO4.  If the drive can't do that, then
	// it's not gonna work too well on this platform anyway...
	MsWrite(MS_IDE0_PIO_DATA_TIMING_REG, 0x020205);
	MsWrite(MS_IDE1_PIO_DATA_TIMING_REG, 0x020205);
	MsWrite(MS_IDE_PIO_COMMAND_TIMING_REG, 0x020205);
	// none of the config calls passes *hwif, which we need to set dmaproc
	// in, so just set the first one in the global list here:
	// FIXME: this doesn't allow for multiple interfaces, really need
	// to fix ide_init_default_hwifs, rather than hacking it like this...
	ide_hwifs[0].dmaproc = fpga_dmaproc;
	ide_hwifs[0].resetproc = fpga_resetproc;
	if (GetTivoConfig(kTivoConfigIdeCap, &ide_cap)) {
		ide_hwifs[0].udma_four = 0;
	} else {
		ide_cap &= kTivoConfigIdeCapAta66;
		ide_hwifs[0].udma_four = ide_cap ? 1 : 0;
	}

#ifdef SUPPORT_SG
	if (!ide_hwifs[0].sg_table) {
		void *sg;

		sg = kmalloc(sizeof(struct fpga_sgent)*SG_ENTRIES, GFP_KERNEL);
		if (!sg) {	// out of kernel memory this early? very bad.
			ide_hwifs[0].dmaproc = NULL;
			ide_hwifs[0].resetproc = NULL;
		}
		ide_hwifs[0].sg_table = sg;
		stop_dma();
	}
	ide_hwifs[0].tivo.version = IDETIVO_DRIVER_VERSION;
	ide_hwifs[0].tivo.sgops = &fpga_sgops;
#endif
#ifdef DEBUG_IDE_REGS_WITH_GPIO11
	MsOrReg(MS_GPIOOBE_REG, 0x0800);
	MsAndReg(MS_GPIOOB_REG, ~0x0800);
#endif
}

static int fpga_ide_request_irq(unsigned int irq,
                                void (*handler)(int,void *, struct pt_regs *),
                                unsigned long flags, const char *device,
                                void *dev_id)
{
	int retval;

	real_intr_handler = handler;
	// This is always IRQ_MS_IDE:
	retval = request_irq(irq, drive_done_intr, flags, device, dev_id);
	if (retval)
		return retval;
	retval = request_irq(IRQ_MS_IDEDI, dma_done_intr, SA_INTERRUPT,
	                     device, dev_id);
	if (retval)
		return retval;
	retval = request_irq(IRQ_MSE_DFE0, dma_err_intr, SA_INTERRUPT,
	                     device, dev_id);
	if (retval)
		return retval;
	retval = request_irq(IRQ_MSE_DFE1, dma_err_intr, SA_INTERRUPT,
	                     device, dev_id);
	return retval;
}

static void fpga_ide_free_irq(unsigned int irq, void *dev_id)
{
	free_irq(IRQ_MS_IDEDI, dev_id);
	free_irq(IRQ_MSE_DFE0, dev_id);
	free_irq(IRQ_MSE_DFE1, dev_id);
	free_irq(irq, dev_id);
}

static int fpga_ide_check_region(ide_ioreg_t from, unsigned int extent)
{
	return 0;
}

static void fpga_ide_request_region(ide_ioreg_t from, unsigned int extent,
                                    const char *name)
{
	return;
}

static void fpga_ide_release_region(ide_ioreg_t from, unsigned int extent)
{
	return;
}

struct ide_ops bcm_ide_ops = {
	&fpga_ide_default_irq,
	&fpga_ide_default_io_base,
	&fpga_ide_init_hwif_ports,
	&fpga_ide_request_irq,
	&fpga_ide_free_irq,
	&fpga_ide_check_region,
	&fpga_ide_request_region,
	&fpga_ide_release_region
};

void _tivo_ide_out_byte(u8 data, ide_ioreg_t port)
{
	MsWrite(port, (u32)data);
}
EXPORT_SYMBOL(_tivo_ide_out_byte);

u8 _tivo_ide_in_byte(ide_ioreg_t port)
{
	return (u8)MsRead(port);
}
EXPORT_SYMBOL(_tivo_ide_in_byte);

/* This is only ever used for data, rest of regs are 8-bit... */
u16 _tivo_ide_in_word(ide_ioreg_t port)
{
	return *(volatile u32 *)ms_ide_data >> 16;
}

// The following are replacements for the ones removed from drivers/ide/ide.c

/*
 * This is used for most PIO data transfers *from* the IDE interface
 */
void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
	u16 *ptr = (u16 *)buffer;

	while (wcount--) {
		barrier();
		*ptr++ = *(volatile u32 *)ms_ide_data >> 16;
		barrier();
		*ptr++ = *(volatile u32 *)ms_ide_data >> 16;
	}
}

/*
 * This is used for most PIO data transfers *to* the IDE interface
 */
void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
	u16 *ptr = (u16 *)buffer;

	while (wcount--) {
		*(volatile u32 *)ms_ide_data = *ptr++ << 16;
		barrier();
		*(volatile u32 *)ms_ide_data = *ptr++ << 16;
		barrier();
	}
}

/*
 * 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.
 */
void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
	unsigned short *ptr = (unsigned short *) buffer;
	unsigned int wcount;

	++bytecount;
	wcount = (bytecount / 4) << 1;

	while (wcount--) {
		*ptr++ = *(volatile u32 *)ms_ide_data >> 16;
		barrier();
	}
	if ((bytecount & 0x03) >= 2) {
		*ptr++ = *(volatile u32 *)ms_ide_data >> 16;
		barrier();
	}
}

void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
	unsigned short *ptr = (unsigned short *) buffer;
	unsigned int wcount;

	++bytecount;
	wcount = (bytecount / 4) << 1;

	while (wcount--) {
		*(volatile u32 *)ms_ide_data = *ptr++ << 16;
		barrier();
	}
	if ((bytecount & 0x03) >= 2) {
		*(volatile u32 *)ms_ide_data = *ptr++ << 16;
		barrier();
	}
}

