/*
 * sata_initio162x.c - Driver for Initio 162x SATA controllers
 */
/* Change Log
   - 1. First Release
   - 2. Fixed port 2 issue
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <linux/blkdev.h>
#include <scsi/scsi_device.h>
#include <linux/version.h>

#define DRV_NAME	"sata_initio162x"
#define DRV_VERSION	"0.5"

//#define TRACE_PRINTK(fmt, args...) printk(KERN_ERR "(Nic)%s: " fmt, __FUNCTION__, ## args)
#define TRACE_PRINTK(fmt, args...)

enum {
	MMIO_BAR_PCI		= 5,
	MMIO_BAR_CARDBUS		= 1,
	
	NR_PORTS		= 2,

	IDMA_CPB_BYTES		= 32,
	IDMA_PRD_BYTES		= LIBATA_MAX_PRD * 8,
	IDMA_PKT_BYTES		= IDMA_CPB_BYTES + IDMA_PRD_BYTES,

  INIC_DMA_BOUNDARY  = 0xffffff,

	HOST_ACTRL	= 0x8,
	HOST_CTL		= 0x7c,
	HOST_STAT		= 0x7e,
	HOST_IRQ_STAT		= 0xbc,
	HOST_IRQ_MASK		= 0xbe,

	PORT_SIZE		= 0x40,

	/* registers for ATA TF operation */
	PORT_TF			= 0x00,
	PORT_ALT_STAT		= 0x08,
	PORT_IRQ_STAT		= 0x09,
	PORT_IRQ_MASK		= 0x0a,
	PORT_PRD_CTL		= 0x0b,
	PORT_PRD_ADDR		= 0x0c,
	PORT_PRD_XFERLEN	= 0x10,
  PORT_CPB_CPBLAR   = 0x18,
  PORT_CPB_PTQFIFO  = 0x1c,
	/* IDMA register */
	PORT_IDMA_CTL		= 0x14,

	PORT_SCR		= 0x20,

	/* HOST_CTL bits */
	HCTL_IRQOFF		= (1 << 8),  /* global IRQ off */
	HCTL_PWRDWN		= (1 << 12), /* power down PHYs */
	HCTL_SOFTRST		= (1 << 13), /* global reset (no phy reset) */
	HCTL_RPGSEL		= (1 << 15), /* register page select */

	HCTL_KNOWN_BITS		= HCTL_IRQOFF | HCTL_PWRDWN | HCTL_SOFTRST |
				  HCTL_RPGSEL,

	/* HOST_IRQ_(STAT|MASK) bits */
	HIRQ_PORT0		= (1 << 0),
	HIRQ_PORT1		= (1 << 1),
	HIRQ_SOFT		= (1 << 14),
	HIRQ_GLOBAL		= (1 << 15), /* STAT only */

	/* PORT_IRQ_(STAT|MASK) bits */
	PIRQ_OFFLINE		= (1 << 0),  /* device unplugged */
	PIRQ_ONLINE		= (1 << 1),  /* device plugged */
	PIRQ_COMPLETE		= (1 << 2),  /* completion interrupt */
	PIRQ_FATAL		= (1 << 3),  /* fatal error */
	PIRQ_ATA		= (1 << 4),  /* ATA interrupt */
	PIRQ_REPLY		= (1 << 5),  /* reply FIFO not empty */
	PIRQ_PENDING		= (1 << 7),  /* port IRQ pending (STAT only) */

	PIRQ_ERR		= PIRQ_OFFLINE | PIRQ_ONLINE | PIRQ_FATAL,

	PIRQ_MASK_DMA_READ	= PIRQ_REPLY | PIRQ_ATA,
	PIRQ_MASK_OTHER		= PIRQ_REPLY | PIRQ_COMPLETE,
	PIRQ_MASK_FREEZE	= 0xff,

	/* PORT_PRD_CTL bits */
	PRD_CTL_START		= (1 << 0),
	PRD_CTL_WR		= (1 << 3),
	PRD_CTL_DMAEN		= (1 << 7),  /* DMA enable */

	/* PORT_IDMA_CTL bits */
	IDMA_CTL_RST_ATA	= (1 << 2),  /* hardreset ATA bus */
	IDMA_CTL_RST_IDMA	= (1 << 5),  /* reset IDMA machinary */
	IDMA_CTL_PSE		= (1 << 6), 
	IDMA_CTL_GO		= (1 << 7),  /* IDMA mode go */
	IDMA_CTL_ATA_NIEN	= (1 << 8),  /* ATA IRQ disable */
	/* CPB Control Flags*/
	cVLD = (1 << 0),  /* CPB VALID */
	cQUE = (1 << 1),  /* QUEUED COMMAND */
	cDAT = (1 << 2),  /* DATA */
	cIEN = (1 << 3),  /* PCI Interrupt Enable */
	DEVDIR = (1 << 4), /*Device Direction Control */
	
	/* PDR Control Flags */
	pIGEX = (1 << 25), 
	pPKT  = (1 << 26),
	pDINT = (1 << 27),
	pORD  = (1 << 28),
	pDIRO = (1 << 29),
	pIOM  = (1 << 30),
	pEND  = (1 << 31),
};

unsigned int iobar;

struct initio_host_priv {
	u16	cached_hctl;
};

struct initio_port_priv {
	u8			*pkt;		
  dma_addr_t		pkt_dma;
  u32     *ptable;
  dma_addr_t    cpbtable;
	u8	dfl_prdctl;
	u8	cached_prdctl;
	u8	cached_pirq_mask;
};

static int initio_slave_config(struct scsi_device *sdev)
{
	/* This controller is braindamaged.  dma_boundary is 0xffff
	 * like others but it will lock up the whole machine HARD if
	 * 65536 byte PRD entry is fed.  Reduce maximum segment size.
	 */
	blk_queue_max_segment_size(sdev->request_queue, 65536 - 512);

	return ata_scsi_slave_config(sdev);
}

static struct scsi_host_template initio_sht = {
	.module			= THIS_MODULE,
	.name			= DRV_NAME,
	.ioctl			= ata_scsi_ioctl,
	.queuecommand		= ata_scsi_queuecmd,

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
	.eh_strategy_handler	= ata_scsi_error,
	.max_sectors		= ATA_MAX_SECTORS,
#endif

	.can_queue		= ATA_DEF_QUEUE,
	.this_id		= ATA_SHT_THIS_ID,
	.sg_tablesize		= LIBATA_MAX_PRD,
	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
	.emulated		= ATA_SHT_EMULATED,
	.use_clustering		= ATA_SHT_USE_CLUSTERING,
	.proc_name		= DRV_NAME,
	.dma_boundary		= INIC_DMA_BOUNDARY,
	.slave_configure	= initio_slave_config,
	.bios_param		= ata_std_bios_param,
};

static const int scr_map[] = {
	[SCR_STATUS]	= 0,
	[SCR_ERROR]	= 1,
	[SCR_CONTROL]	= 2,
};

static void __iomem * initio_port_base(struct ata_port *ap)
{
	return ap->host_set->mmio_base + ap->port_no * PORT_SIZE;
}

static void __initio_set_pirq_mask(struct ata_port *ap, u8 mask)
{
	void __iomem *port_base = initio_port_base(ap);
	struct initio_port_priv *pp = ap->private_data;

	writeb(mask, port_base + PORT_IRQ_MASK);
	pp->cached_pirq_mask = mask;
}

static void initio_set_pirq_mask(struct ata_port *ap, u8 mask)
{
	struct initio_port_priv *pp = ap->private_data;

	if (pp->cached_pirq_mask != mask)
		__initio_set_pirq_mask(ap, mask);
}

static void initio_reset_port(void __iomem *port_base)
{
	void __iomem *idma_ctl = port_base + PORT_IDMA_CTL;
	u16 ctl;

	ctl = readw(idma_ctl);	
	ctl &= ~(IDMA_CTL_RST_IDMA | IDMA_CTL_ATA_NIEN | IDMA_CTL_GO);

	/* mask IRQ and assert reset */
	writew(ctl | IDMA_CTL_RST_IDMA | IDMA_CTL_ATA_NIEN, idma_ctl);
	readw(idma_ctl); /* flush */
	
	/* give it some time */
	msleep(1);

	/* release reset */
	writew(ctl | IDMA_CTL_ATA_NIEN, idma_ctl);

	/* clear irq */
	writeb(0xff, port_base + PORT_IRQ_STAT);

	/* reenable ATA IRQ, turn off IDMA mode */
	writew(ctl, idma_ctl);
}

static u32 initio_scr_read(struct ata_port *ap, unsigned sc_reg)
{
	void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr;
	void __iomem *addr;
	u32 val;

//TRACE_PRINTK("initio_scr_read .. (scr_addr==%x)\n",scr_addr);
//TRACE_PRINTK("initio_scr_read .. (sc_reg==%x)\n", sc_reg);

	if (unlikely(sc_reg >= ARRAY_SIZE(scr_map)))
		return 0xffffffffU;

	addr = scr_addr + scr_map[sc_reg] * 4;
	val = readl(scr_addr + scr_map[sc_reg] * 4);

	return val;
}

static void initio_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
{
	void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr;

	if (unlikely(sc_reg >= ARRAY_SIZE(scr_map)))
		return;

	writel(val, scr_addr + scr_map[sc_reg] * 4);
}

/*
 * In TF mode, initio162x is very similar to SFF device.  TF registers
 * function the same.  DMA engine behaves similary using the same PRD
 * format as BMDMA but different command register, interrupt and event
 * notification methods are used.  The following initio_bmdma_*()
 * functions do the impedance matching.
 */
static void initio_bmdma_setup(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct initio_port_priv *pp = ap->private_data;
	void __iomem *port_base = initio_port_base(ap);
	int rw = qc->tf.flags & ATA_TFLAG_WRITE;
  	struct scatterlist	*sg = qc->__sg;
  	u32 sg_len = 0;
	u32 count=0;
	/* make sure device sees PRD table writes */
	wmb();
//  writel(ap->prd_dma, port_base + PORT_PRD_ADDR);
	/* load transfer length */

	for(count=0;count<qc->n_elem;count++)
	{
		sg_len += sg_dma_len(sg);
		sg++;
	}

	writel(sg_len, port_base + PORT_PRD_XFERLEN);

	/* turn on DMA and specify data direction */
	pp->cached_prdctl = pp->dfl_prdctl | PRD_CTL_DMAEN;

	if (!rw)
		pp->cached_prdctl |= PRD_CTL_WR;
		
	writeb(pp->cached_prdctl, port_base + PORT_PRD_CTL);

	/* issue r/w command */
	ap->ops->exec_command(ap, &qc->tf);
}

static void initio_bmdma_start(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct initio_port_priv *pp = ap->private_data;
	void __iomem *port_base = initio_port_base(ap);

	/* start host DMA transaction */
	pp->cached_prdctl |= PRD_CTL_START;
	writeb(pp->cached_prdctl, port_base + PORT_PRD_CTL);
}

static void initio_bmdma_stop(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct initio_port_priv *pp = ap->private_data;
	void __iomem *port_base = initio_port_base(ap);
  u8			dma_stat = 0;
	/* stop DMA engine */
	writeb(pp->dfl_prdctl, port_base + PORT_PRD_CTL);
	dma_stat = readb(port_base + PORT_IRQ_STAT);
  writeb(dma_stat, port_base + PORT_IRQ_STAT);
  dma_stat = readb(port_base + 0x16);
}

static u8 initio_bmdma_status(struct ata_port *ap)
{
	/* event is already verified by the interrupt handler */
	return ATA_DMA_INTR;
}

static void initio_irq_clear(struct ata_port *ap)
{
	TRACE_PRINTK("\n");
	/* noop */
}

static void initio_host_intr(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);
//	struct ata_eh_info *ehi = &ap->eh_info;
	u8 irq_stat;
	u16 idma_status;
	/* fetch and clear irq */
	irq_stat = readb(port_base + PORT_IRQ_STAT);
	writeb(irq_stat, port_base + PORT_IRQ_STAT);
	idma_status = readw(port_base + 0x16);

	if (likely(!(irq_stat & PIRQ_ERR))) {
		struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);

		if (unlikely(!qc)) {
			ata_chk_status(ap);	/* clear ATA interrupt */
			return;
		}
    if(idma_status & 0x80){
       ata_qc_complete(qc);
       return;
    }
		if (likely(ata_host_intr(ap, qc)))
			return;

		ata_chk_status(ap);	/* clear ATA interrupt */

		return;
	}

	/* error */
//ata_ehi_push_desc(ehi, "irq_stat=0x%x", irq_stat);

}

static irqreturn_t initio_interrupt(int irq, void *dev_instance, struct pt_regs *regs )
{

	struct ata_host_set *host_set = dev_instance;
	void __iomem *mmio_base = host_set->mmio_base;
	u16 host_irq_stat;
	u8 ata_mask_irq_stat,rpqfifo,rpqcnt;
	int i, handled = 0;;
  
	host_irq_stat = readw(mmio_base + HOST_IRQ_STAT);

	if (unlikely(!(host_irq_stat & HIRQ_GLOBAL)))
		goto out;
	spin_lock(&host_set->lock);

	for (i = 0; i < NR_PORTS; i++) {
		struct ata_port *ap = host_set->ports[i];
    struct initio_port_priv *pp;
    void __iomem *port_base = initio_port_base(ap);
    pp = ap->private_data;
		if (!(host_irq_stat & (HIRQ_PORT0 << i)))
			continue;
    	ata_mask_irq_stat = readb(port_base + PORT_IRQ_STAT);
    	rpqfifo = readb(port_base + 0x1e);
		rpqcnt = readb(port_base + 0x1f);
  		writew(0x00,port_base + 0x14);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
		if (likely(ap && !(ap->flags & ATA_FLAG_PORT_DISABLED))) {
#else
		if (likely(ap && !(ap->flags & ATA_FLAG_DISABLED))) {			
#endif
			//ATA_FLAG_PORT_ENABLED
			initio_host_intr(ap);
			handled++;
		} else {
			if (ata_ratelimit())
				dev_printk(KERN_ERR, host_set->dev, "interrupt "
					   "from disabled port %d (0x%x)\n",
					   i, host_irq_stat);
		}
	}

	spin_unlock(&host_set->lock);

 out:
	return IRQ_RETVAL(handled);
}

static inline void initio_packet_start(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	void __iomem *port_base = initio_port_base(ap);
	void __iomem *idma_ctl = port_base + PORT_IDMA_CTL;

	/* fire up the ADMA engine */
	writew(0x400, port_base + HOST_CTL);
	writew(IDMA_CTL_GO, idma_ctl);
  writeb(0, port_base + PORT_CPB_PTQFIFO);
}

static int initio_qc_issue(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
TRACE_PRINTK("\n");
	/* ATA IRQ doesn't wait for DMA transfer completion and vice
	 * versa.  Mask IRQ selectively to detect command completion.
	 * Without it, ATA DMA read command can cause data corruption.
	 *
	 * Something similar might be needed for ATAPI writes.  I
	 * tried a lot of combinations but couldn't find the solution.
	 */

	initio_set_pirq_mask(ap, 0x20);
	switch (qc->tf.protocol) {
	case ATA_PROT_DMA:
	  initio_set_pirq_mask(ap, 0x00);
		initio_packet_start(qc);
		return 0;

	case ATA_PROT_ATAPI_DMA:
		BUG();
		break;

	default:
		break;
	}
	/* Issuing a command to yet uninitialized port locks up the
	 * controller.  Most of the time, this happens for the first
	 * command after reset which are ATA and ATAPI IDENTIFYs.
	 * Fast fail if stat is 0x7f or 0xff for those commands.
	 */
	if (unlikely(qc->tf.command == ATA_CMD_ID_ATA ||
		     qc->tf.command == ATA_CMD_ID_ATAPI)) {
		u8 stat = ata_chk_status(ap);
		if (stat == 0x7f || stat == 0xff)
			return -1;
	}

	return ata_qc_issue_prot(qc);
}
#if 0

static void initio_freeze(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);

	__initio_set_pirq_mask(ap, PIRQ_MASK_FREEZE);

	ata_chk_status(ap);
	writeb(0xff, port_base + PORT_IRQ_STAT);

	readb(port_base + PORT_IRQ_STAT); /* flush */
}

static void initio_thaw(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);

	ata_chk_status(ap);
	writeb(0xff, port_base + PORT_IRQ_STAT);

	__initio_set_pirq_mask(ap, PIRQ_MASK_OTHER);

	readb(port_base + PORT_IRQ_STAT); /* flush */
}

static void initio_post_internal_cmd(struct ata_queued_cmd *qc)
{
	/* make DMA engine forget about the failed command */
	if (qc->err_mask)
		initio_reset_port(initio_port_base(qc->ap));
}

static int initio_port_resume(struct ata_port *ap)
{
	init_port(ap);
	return 0;
}

/*
 * SRST and SControl hardreset don't give valid signature on this
 * controller.  Only controller specific hardreset mechanism works.
 */
static int initio_hardreset(struct ata_port *ap, unsigned int *class)
{
	void __iomem *port_base = initio_port_base(ap);
	void __iomem *idma_ctl = port_base + PORT_IDMA_CTL;

	u16 val;
	int rc;

	/* hammer it into sane state */
	initio_reset_port(port_base);

	val = readw(idma_ctl);
	writew(val | IDMA_CTL_RST_ATA, idma_ctl);
	readw(idma_ctl);	/* flush */
	msleep(1);
	writew(val & ~IDMA_CTL_RST_ATA, idma_ctl);
//	writew(val | IDMA_CTL_PSE, idma_ctl);

	return 0;
}
#endif

static void initio_error_handler(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);
	struct initio_port_priv *pp = ap->private_data;
	struct ata_taskfile tf;
TRACE_PRINTK("\n");
	/* reset PIO HSM and stop DMA engine */
	initio_reset_port(port_base);
	TRACE_PRINTK("initio_reset_port done\n");
	ap->hsm_task_state = HSM_ST_IDLE;
	writeb(pp->dfl_prdctl, port_base + PORT_PRD_CTL);
	/* PIO and DMA engines have been stopped, perform recovery */

	__sata_phy_reset(ap);

  tf.lbah = inb((unsigned long) ap->ioaddr.lbah_addr);
  tf.lbam = inb((unsigned long) ap->ioaddr.lbam_addr);
  tf.lbal = inb((unsigned long) ap->ioaddr.lbal_addr);
  tf.nsect = inb((unsigned long) ap->ioaddr.nsect_addr);
	
	ap->device->class = ata_dev_classify(&tf);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)	
  if (!ata_dev_present(ap->device)) {
#else
  if (!ata_dev_enabled(ap->device)) {
#endif  	
        VPRINTK("Port disabled post-sig: No device present.\n");
        ata_port_disable(ap);
  }
	
}

static void initio_dev_config(struct ata_port *ap, struct ata_device *dev)
{
	TRACE_PRINTK("\n");
	/* initio can only handle upto LBA28 max sectors */
}

static void init_port(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);
  struct initio_port_priv *pp = ap->private_data;

	/* Setup PRD address */
	writel(ap->prd_dma, port_base + PORT_PRD_ADDR);
	/* Setup CPB address */
	writel(pp->cpbtable, port_base + PORT_CPB_CPBLAR);	
}



static int initio_port_start(struct ata_port *ap)
{
	void __iomem *port_base = initio_port_base(ap);
	struct initio_port_priv *pp;
	struct device *dev = ap->host_set->dev;
	u8 tmp;
	int rc=0;

	/* Alloc resources */
	rc = ata_port_start(ap);
	
	if (rc)
		return rc;

	/* alloc and initialize private data */
	pp = kzalloc(sizeof(*pp), GFP_KERNEL);
	if (!pp){
		rc = -ENOMEM;
		goto err_out;
	}
	pp->pkt = dma_alloc_coherent(dev, IDMA_PKT_BYTES, &pp->pkt_dma,
								GFP_KERNEL);
	if (!pp->pkt)
		goto err_out_kfree;
	/* paranoia? */
	if ((pp->pkt_dma & 7) != 0) {
		printk("bad alignment for pp->pkt_dma: %08x\n",
						(u32)pp->pkt_dma);
		dma_free_coherent(dev, IDMA_PKT_BYTES,
						pp->pkt, pp->pkt_dma);
		goto err_out_kfree;
	}
	memset(pp->pkt, 0, IDMA_PKT_BYTES);		
	ap->private_data = pp;

  pp->ptable = dma_alloc_coherent(dev, 4*32, &pp->cpbtable,
								GFP_KERNEL);
	if ((pp->cpbtable & 3) != 0) {
		printk("bad alignment for pp->cpbtable: %08x\n",
						(u32)pp->cpbtable);
		dma_free_coherent(dev, 4*32,
						pp->ptable, pp->cpbtable);
		goto err_out_kfree;
	}								
	memset(pp->ptable, 0, 4*32);		
	/* default PRD_CTL value, DMAEN, WR and START off */
	tmp = readb(port_base + PORT_PRD_CTL);
	tmp &= ~(PRD_CTL_DMAEN | PRD_CTL_WR | PRD_CTL_START);
	pp->dfl_prdctl = tmp;

	init_port(ap);

	return 0;
err_out_kfree:
	kfree(pp);
err_out:
	ata_port_stop(ap);
	return rc;	
}
static void initio_fill_sg(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct initio_port_priv *pp = ap->private_data;
	u8  *buf = pp->pkt;
	int i = 32;	
	struct scatterlist *sg;
	unsigned int idx;
  int rw = qc->tf.flags & ATA_TFLAG_WRITE;
  u32 flags=pORD;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
  assert(qc->__sg != NULL);
	assert(qc->n_elem > 0 || qc->pad_len > 0);
#else
  WARN_ON(qc->__sg == NULL);
	WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
#endif  

  idx = 0;
	ata_for_each_sg(sg, qc) {
		u32 addr, len;

		addr = (u32)sg_dma_address(sg);
		*(__le32 *)(buf + i) = cpu_to_le32(addr);
		i += 4;

		len = sg_dma_len(sg);
		*(__le32 *)(buf + i) = cpu_to_le32(len & 0xffff);

		if (ata_sg_is_last(sg, qc))
			flags |= pEND;
    if (rw)
      flags |= pDIRO ;
    *(__le32 *)(buf + i) |= flags;
    i += 4;
		idx++;			
    }
}
static void initio_qc_prep(struct ata_queued_cmd *qc)
{
  struct initio_port_priv *pp = qc->ap->private_data;
	u8  *buf = pp->pkt;
  void __iomem *port_base = initio_port_base(qc->ap);
	u32 pkt_dma = (u32)pp->pkt_dma;
	int i = 0;
  struct scatterlist	*sg = qc->__sg;
  u32 sg_len = 0;
	u32 count=0;
	
	VPRINTK("ENTER\n");

	if (qc->tf.protocol != ATA_PROT_DMA) {
		ata_qc_prep(qc);
		return;
	}

	buf[i++] = 0;	/* Response flags */
	buf[i++] = 0;	/* ATA Error*/
	buf[i++] = 0;	/* ATA Status*/
	buf[i++] = cVLD | cDAT | cIEN;
	
  i += 4; /* cTLEN, gets filled in below */
  i += 4; /* cPRD */
  

  buf[i++] = 0;	/* reserved */
  buf[i++] = 0;	/* reserved */
  buf[i++] = 0;	/* reserved */
  buf[i++] = 0;	/* reserved */


  buf[i++] = qc->tf.feature;
  if ((qc->tf.flags & ATA_TFLAG_LBA48))
     buf[i++] = qc->tf.hob_feature;
  else
     buf[i++] = 0;
  buf[i++] = qc->tf.device;
  buf[i++] = 0;

  
  buf[i++] = qc->tf.nsect;
  if ((qc->tf.flags & ATA_TFLAG_LBA48))
     buf[i++] = qc->tf.hob_nsect;
  else
     buf[i++] = 0;  
  buf[i++] = qc->tf.lbal;
  if ((qc->tf.flags & ATA_TFLAG_LBA48))
     buf[i++] = qc->tf.hob_lbal;
  else
     buf[i++] = 0;    


  buf[i++] = qc->tf.lbam;
  if ((qc->tf.flags & ATA_TFLAG_LBA48))
     buf[i++] = qc->tf.hob_lbam;
  else
     buf[i++] = 0;  
  buf[i++] = qc->tf.lbah;
  if ((qc->tf.flags & ATA_TFLAG_LBA48))
     buf[i++] = qc->tf.hob_lbah;
  else
     buf[i++] = 0;    


  buf[i++] = qc->tf.command;
//  buf[i++] = qc->tf.ctl;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  *(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i);	/* cPRD */
	initio_fill_sg(qc);
	wmb();	/* flush PRDs and pkt to memory */
	for(count=0;count<qc->n_elem;count++)
	{
		sg_len += sg_dma_len(sg);
		sg++;
	}		
  *(__le32 *)(buf+4) = cpu_to_le32(sg_len);
  i = i + 8*qc->n_elem;
	/* Setup CPB Count */
	pp->ptable[0] = pp->pkt_dma;
	writel(pp->cpbtable, port_base + PORT_CPB_CPBLAR);	

#if 0
	/* dump out CPB + PRDs for debug */
	{
		int j, len = 0;
		static char obuf[2048];
		for (j = 0; j < i; ++j) {
			len += sprintf(obuf+len, "%02x ", buf[j]);
			if ((j & 7) == 7) {
				printk("%s\n", obuf);
				len = 0;
			}
		}
		if (len)
			printk("%s\n", obuf);
	}
#endif		
}

u8 initio_ata_check_status(struct ata_port *ap)
{
	return inb(ap->ioaddr.status_addr);
}
u8 initio_ata_check_altstatus(struct ata_port *ap)
{
	if (iobar == MMIO_BAR_CARDBUS)
		return readb((void __iomem *)ap->ioaddr.altstatus_addr);
	else
		return inb(ap->ioaddr.altstatus_addr);
}

static struct ata_port_operations initio_port_ops = {
	.port_disable		= ata_port_disable,
	.tf_load		= ata_tf_load,
	.tf_read		= ata_tf_read,
	.check_status		= initio_ata_check_status,
	.check_altstatus = initio_ata_check_altstatus,
	.exec_command		= ata_exec_command,
	.dev_select		= ata_std_dev_select,
  .phy_reset		= initio_error_handler,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
  .error_handler= initio_error_handler,
#endif  
	.scr_read		= initio_scr_read,
	.scr_write		= initio_scr_write,

	.bmdma_setup		= initio_bmdma_setup,
	.bmdma_start		= initio_bmdma_start,
	.bmdma_stop		= initio_bmdma_stop,
	.bmdma_status		= initio_bmdma_status,
	.irq_handler		= initio_interrupt,
	.irq_clear		= initio_irq_clear,

	.qc_prep	 	= initio_qc_prep,
	.qc_issue		= initio_qc_issue,
	.eng_timeout	= ata_eng_timeout,
	.dev_config		= initio_dev_config,
	.port_start		= initio_port_start,
	.port_stop		= ata_port_stop,
	.host_stop    = ata_host_stop,
};

static struct ata_port_info initio_port_info = {
	.sht			= &initio_sht,
	/* For some reason, ATA_PROT_ATAPI is broken on this
	 * controller, and no, PIO_POLLING does't fix it.  It somehow
	 * manages to report the wrong ireason and ignoring ireason
	 * results in machine lock up.  Tell libata to always prefer
	 * DMA.
	 */
	.host_flags	= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA ,
	.pio_mask		= 0x1f,	/* pio0-4 */
	.mwdma_mask		= 0x07, /* mwdma0-2 */
	.udma_mask		= 0x7f,	/* udma0-6 */
	.port_ops		= &initio_port_ops
};

static int init_controller(void __iomem *mmio_base, u16 hctl)
{
	int i;
	u16 val;

	hctl &= ~HCTL_KNOWN_BITS;

	/* Soft reset whole controller.  Spec says reset duration is 3
	 * PCI clocks, be generous and give it 10ms.
	 */
	writew(hctl | HCTL_SOFTRST, mmio_base + HOST_CTL);
	readw(mmio_base + HOST_CTL); /* flush */
	
	for (i = 0; i < 10; i++) {
		msleep(1);
		val = readw(mmio_base + HOST_CTL);
		if (!(val & HCTL_SOFTRST))
			break;
	}
	
	if (val & HCTL_SOFTRST)
		return -EIO;

	/* mask all interrupts and reset ports */
	for (i = 0; i < NR_PORTS; i++) {
		void __iomem *port_base = mmio_base + i * PORT_SIZE;

		writeb(0xff, port_base + PORT_IRQ_MASK);
		initio_reset_port(port_base);
	}

	/* port IRQ is masked now, unmask global IRQ */
	writew(hctl & ~HCTL_IRQOFF , mmio_base + HOST_CTL);
	val = readw(mmio_base + HOST_IRQ_MASK);
	val &= ~(HIRQ_PORT0 | HIRQ_PORT1);
	writew(val, mmio_base + HOST_IRQ_MASK);

	return 0;
}

static int initio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	static int printed_version = 0;
	struct ata_port_info *pinfo = &initio_port_info;
	struct ata_probe_ent *probe_ent;
	struct initio_host_priv *hpriv;
	void __iomem *io_base1 = NULL;
	void __iomem *io_base2 = NULL;
	void __iomem *port_base = NULL;
	void __iomem *io_base = NULL;
	int i, rc;

	if (!printed_version++)
		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");

	rc = pci_enable_device(pdev);
	if (rc)
		return rc;

  rc = pci_request_regions(pdev, DRV_NAME);

	if (rc)
		goto err_out;

	if ((pci_resource_flags(pdev, MMIO_BAR_PCI) & IORESOURCE_MEM) == 0) 
	{
		pci_release_regions(pdev);
		if ((pci_resource_flags(pdev, MMIO_BAR_CARDBUS) & IORESOURCE_MEM) == 0) 
		{
			rc = -ENODEV;
			goto err_out_regions;
		}
		else
		{			
			iobar = MMIO_BAR_CARDBUS;
		}
	}
	else
	{
		iobar = MMIO_BAR_PCI;
	}

	port_base = pci_iomap(pdev, iobar, 0);
	if (port_base == NULL) {
		TRACE_PRINTK("pci_iomap: error\n");
		rc = -ENOMEM;
		goto err_out_regions;
	}
	TRACE_PRINTK("pci_iomap: no error\n");



	/* Set dma_mask.  This devices doesn't support 64bit addressing. */
	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
	if (rc) {
		dev_printk(KERN_ERR, &pdev->dev, "32-bit DMA enable failed\n");
		goto err_out_iounmap;
	}

	rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
	if (rc) {
		dev_printk(KERN_ERR, &pdev->dev, "32-bit consistent DMA enable failed\n");
		goto err_out_iounmap;
	}

	probe_ent = kcalloc(1, sizeof(*probe_ent), GFP_KERNEL);
	hpriv = kcalloc(1, sizeof(*hpriv), GFP_KERNEL);
	if (!probe_ent || !hpriv)
		return -ENOMEM;

	probe_ent->dev = &pdev->dev;
	INIT_LIST_HEAD(&probe_ent->node);

	probe_ent->sht			= pinfo->sht;
	probe_ent->host_flags		= pinfo->host_flags;
	probe_ent->pio_mask		= pinfo->pio_mask;
	probe_ent->mwdma_mask		= pinfo->mwdma_mask;
	probe_ent->udma_mask		= pinfo->udma_mask;
	probe_ent->port_ops		= pinfo->port_ops;
	probe_ent->n_ports		= NR_PORTS;

	probe_ent->irq = pdev->irq;
	probe_ent->irq_flags = SA_SHIRQ;
  probe_ent->mmio_base	= port_base;

	if (iobar == MMIO_BAR_CARDBUS)
	{
		io_base = pci_iomap(pdev, 0, 0xFF);	
		for (i = 0; i < NR_PORTS; i++) {
			struct ata_ioports *port = &probe_ent->port[i];
			void __iomem *port_base_inx = port_base + i * PORT_SIZE;
			void __iomem *io_base_inx = io_base + i * PORT_SIZE;
			port->cmd_addr = (unsigned long)io_base_inx;
			port->altstatus_addr = port->ctl_addr = (unsigned long)(void __iomem *)((unsigned long)(port_base_inx + HOST_ACTRL));
			port->scr_addr = (unsigned long)(port_base_inx + PORT_SCR);
	
			ata_std_ports(&probe_ent->port[i]);
		}
	}
	else
	{
		for (i = 0; i < NR_PORTS; i++) {
			struct ata_ioports *port = &probe_ent->port[i];
			void __iomem *port_base_inx = port_base + i * PORT_SIZE;
	    io_base1 = pci_iomap(pdev, 2 * i, 0);
			port->cmd_addr = (unsigned long)io_base1;
			io_base2 = pci_iomap(pdev, 2 * i+1, 0);
		port->altstatus_addr =
		port->ctl_addr = (unsigned long)(void __iomem *)((unsigned long)io_base2 | ATA_PCI_CTL_OFS);
		port->scr_addr = (unsigned long)(port_base_inx + PORT_SCR);
	
		ata_std_ports(port);
		}
	}

	probe_ent->private_data = hpriv;
	hpriv->cached_hctl = readw(port_base + HOST_CTL);

	rc = init_controller(port_base, hpriv->cached_hctl);
	if (rc) {
		dev_printk(KERN_ERR, &pdev->dev, "failed to initialize controller\n");
		return rc;
	}

	pci_set_master(pdev);

	rc = ata_device_add(probe_ent);
	kfree(probe_ent);
	if (rc != NR_PORTS)
		goto err_out_iounmap;
	return 0;
	
err_out_iounmap:
	pci_iounmap(pdev, port_base);
err_out_regions:
	pci_release_regions(pdev);
err_out:
	pci_disable_device(pdev);
	return rc;
	
}
	
static const struct pci_device_id initio_pci_tbl[] = {
	{ PCI_VENDOR_ID_INIT, 0x1622, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
	{ },
};

static struct pci_driver initio_pci_driver = {
	.name 		= DRV_NAME,
	.id_table	= initio_pci_tbl,
	.probe 		= initio_init_one,
	.remove		= ata_pci_remove_one,
};

static int __init initio_init(void)
{
	printk("initio module init (version %s)\n", DRV_VERSION);
	return pci_module_init(&initio_pci_driver);
}

static void __exit initio_exit(void)
{
	printk("initio module exit \n");
	pci_unregister_driver(&initio_pci_driver);
}

MODULE_AUTHOR("INITIO");
MODULE_DESCRIPTION("low-level driver for Initio 162x SATA");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, initio_pci_tbl);
MODULE_VERSION(DRV_VERSION);

module_init(initio_init);
module_exit(initio_exit);
