/* drivers/ata/sata_pnx833x.c
 *
 * Copyright 2008 NXP Semiconductors
 *	Chris Steel <chris.steel@nxp.com>
 *
 * NXP pnx833x embedded sata controller support
 	Based on previous sata_pnx833x.c by Nikita V. Youshchenko
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <linux/platform_device.h>

#define DRV_NAME			"pnx833x-sata"
#define DRV_VERSION			"0.4"

/* Hardware has limitation of DMA transfer size - 20 sectors */
#define SATA_PNX833X_SG_SIZE	20
/* WARNING! This should not me more than LIBATA_MAX_PRD! */

#define HANDSHAKE_REG(base)		(base + 0x024)

#define DMA_VP_REG(base)		((void __iomem *)(base + 0x030))
#define DMA_STS_REG(base)		((void __iomem *)(base + 0x038))
#define DMA_CTL_REG(base)		((void __iomem *)(base + 0x03c))

#define SATA_STATUS_REG(base)		((void __iomem *)(base + 0x040))
#define SATA_ERROR_REG(base)		((void __iomem *)(base + 0x044))
#define SATA_CONTROL_REG(base)		((void __iomem *)(base + 0x048))
#define SATA_ACTIVE_REG(base)		((void __iomem *)(base + 0x04c))
#define SATA_NOTIFY_REG(base)		((void __iomem *)(base + 0x050))

#define INT_STS_REG(base)		((void __iomem *)(base + 0xfe0))
#define INT_ENA_REG(base)		((void __iomem *)(base + 0xfe4))
#define INT_CLR_REG(base)		((void __iomem *)(base + 0xfe8))
#define SWRESET_REG(base)		((void __iomem *)(base + 0xc00))

/* Bits in handshake register */

#define HS_READ_HANDSHAKE	(1 << 0)	/* word read done */
#define HS_READ_AVAILABLE	(1 << 1)	/* may read */
#define HS_WRITE_READY		(1 << 2)	/* ready to write */
#define HS_PIO_DATA_PHASE	(1 << 3)	/* if zero, some error happened */

/* Bits and values related to DMA_CTL register */

#define DMA_CTRL_START		(1 << 5)	/* bit 4 - DMA_CTRL_START */
#define DMA_DIRECTION_WRITE	(1 << 6)	/* bit 5 - 1 for write to device */
#define DMA_PROTOCOL_ATA	(2 << 7)	/* Value 0010b in bits 10:7 */
#define DMA_RESET		(1 << 16)	/* bit 16 - DMA_RESET */

/* Bits in interrupt status that we are interested in */
/* (macro names are derived from register bit names) */
#define DEVICE_INT_INT		(1 << 0)	/* ATA protocol interrupt */
#define DMA_ERROR_INT		(1 << 1)	/* DMA requested but not ready */
#define BAD_DMA_CMD_INT		(1 << 11)	/* DMA command incompatible with current DMA mode */
#define NO_DEV_INT1		(1 << 4)	/* Attempt to use dev1 */
#define SERROR_E_INT		(1 << 5)	/* Internal error - RX FIFO overrun */
#define SERROR_F_INT		(1 << 6)	/* Error - illegal FIS recieved */
#define SERROR_P_INT		(1 << 7)	/* Error - received FIS not expected by protocol */
#define SERROR_D_INT		(1 << 8)	/* Error - disparity or decoding error */
#define SERROR_C_INT		(1 << 9)	/* Error - CRC mismatch */
#define SERROR_H_INT		(1 << 10)	/* Error - handshake error */
#define DMA0_EOT_INT		(1 << 21)	/* end of dma transaction */

/* Bits in SATA error register - looks like those have to be cleared
   in error interrupt handler */
#define SATA_ERR_SATA_ERR_E	(1 << 11)	/* Corresponds to SERROR_E_INT */
#define SATA_ERR_SATA_DIAG_F	(1 << 25)	/* Corresponds to SERROR_F_INT */
#define SATA_ERR_SATA_ERR_P	(1 << 10)	/* Corresponds to SERROR_P_INT */
#define SATA_ERR_SATA_DIAG_B	(1 << 19)	/* Corresponds to SERROR_D_INT */
#define SATA_ERR_SATA_ERR_C	(1 << 9)	/* Corresponds to SERROR_C_INT */
#define SATA_ERR_SATA_DIAG_H	(1 << 22)	/* Corresponds to SERROR_H_INT */

#define ALL_ERROR_INTERRUPTS	(SERROR_E_INT | SERROR_F_INT | SERROR_P_INT | \
				 /*SERROR_D_INT |*/ SERROR_C_INT | SERROR_H_INT)

#define WANTED_INTERRUPTS	(DEVICE_INT_INT | DMA_ERROR_INT | \
				 BAD_DMA_CMD_INT | NO_DEV_INT1 | \
				 ALL_ERROR_INTERRUPTS | DMA0_EOT_INT)


/* NHP SATA port private data. */

struct sata_pnx833x_port_private_data {

	/* vector pointer table */
#define VPT_ENTRIES 1		/* replace with 32 if ever implementing code
				   that uses all entries of the table */
#define VPT_BYTES (VPT_ENTRIES * sizeof(unsigned long))
	unsigned long *vpt;
	dma_addr_t vpt_dma;

	/* for buffer descriptor table, ata_port::prd does well */
};

static int sata_pnx833x_port_start (struct ata_port *ap)
{
	struct device *dev = ap->host->dev;
	struct sata_pnx833x_port_private_data *pd;
	void __iomem *base = ap->ioaddr.cmd_addr;
	int res = -ENOMEM;

	/* Allocate private data */
	pd = (struct sata_pnx833x_port_private_data *)kmalloc(sizeof(struct sata_pnx833x_port_private_data), GFP_KERNEL);
	if (!pd)
	{
		return -ENOMEM;
	}
	memset(pd, 0, sizeof(struct sata_pnx833x_port_private_data));
	ap->private_data = pd;

	/* Allocate vector pointer table */
	pd->vpt = (unsigned long *)dma_alloc_coherent(dev, VPT_BYTES, &pd->vpt_dma, GFP_KERNEL);
	if (!pd->vpt)
	{
		goto err_vpt;
	}
	/* Allocate the rest */
	res = ata_port_start(ap);
	if (res)
		goto err_portstart;

	/* Reset DMA - just to be sure */
	writel(DMA_RESET, DMA_CTL_REG(base));

	/* Clear all interrupts - just for case */
	writel(0xffffffff, INT_CLR_REG(base));
	
	/* Enable wanted interrupts */
	writel(WANTED_INTERRUPTS, INT_ENA_REG(base));

	return 0;

err_portstart:
	dma_free_coherent(dev, VPT_BYTES, pd->vpt, pd->vpt_dma);
err_vpt:
	ap->private_data = 0;
	kfree(pd);
	return res;
}

void sata_pnx833x_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
{
	struct ata_ioports *ioaddr = &ap->ioaddr;
	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;

	if (tf->ctl != ap->last_ctl) {
		writel(tf->ctl, ioaddr->ctl_addr);
		ap->last_ctl = tf->ctl;
		ata_wait_idle(ap);
	}

	if (is_addr) {
		if (tf->flags & ATA_TFLAG_LBA48) {
			writel(tf->feature, ioaddr->feature_addr);
			writel(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr);
			writel(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr);
			writel(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr);
			writel(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr);
		}
		else
		{
			writel(tf->feature, ioaddr->feature_addr);
			writel(tf->nsect, ioaddr->nsect_addr);
			writel(tf->lbal, ioaddr->lbal_addr);
			writel(tf->lbam, ioaddr->lbam_addr);
			writel(tf->lbah, ioaddr->lbah_addr);
		}
	}

	if (tf->flags & ATA_TFLAG_DEVICE) {
		writel(tf->device, ioaddr->device_addr);
	}

	ata_wait_idle(ap);
}

static inline unsigned short read_word_handshake(void __iomem *data, void __iomem *handshake)
{
	unsigned short res = 0;
	int timeout = 5000000;	/* to avoid hangs if something went wrong */

	do
	{
		unsigned long hs;

		/* Read handshake register */
		hs = readl(handshake);

		if (hs & (HS_PIO_DATA_PHASE | HS_READ_AVAILABLE))
		{
			res = readl(data);
			/* Confirm data read */
			writel(HS_READ_HANDSHAKE, handshake);
			return res;
		}
	} while (--timeout);
	
	return res;
}

static inline void write_word_handshake(void __iomem *data, void __iomem *handshake, unsigned short word)
{
	int timeout = 5000000;	/* to avoid hands if something went wrong */

	do
	{
		/* Read handshake register */
		unsigned long hs = readl(handshake);

		/* Check if hardware is willing to do i/o */
		if (hs & (HS_PIO_DATA_PHASE | HS_WRITE_READY))
		{
			writel((unsigned long)word, data);
			return;
		}
	} while (--timeout);
}

static void sata_pnx833x_data_xfer(struct ata_device *adev, unsigned char *buf,
					unsigned int buflen, int write_data)
{
	struct ata_port *ap = adev->link->ap;
	unsigned int i;
	unsigned int words = buflen >> 1;
	u16 *buf16 = (u16 *) buf;

	/* Transfer multiple of 2 bytes */
	if (write_data) {
		for (i = 0; i < words; i++) {
			write_word_handshake(ap->ioaddr.data_addr, HANDSHAKE_REG(ap->ioaddr.cmd_addr), le16_to_cpu(buf16[i]));
		}
	} else {
		for (i = 0; i < words; i++) {
			buf16[i] = cpu_to_le16(read_word_handshake(ap->ioaddr.data_addr, HANDSHAKE_REG(ap->ioaddr.cmd_addr)));
		}
	}

	/* Transfer trailing 1 byte, if any. */
	if (unlikely(buflen & 0x01)) {
		u16 align_buf[1] = { 0 };
		unsigned char *trailing_buf = buf + buflen - 1;

		if (write_data) {
			memcpy(align_buf, trailing_buf, 1);
			write_word_handshake(ap->ioaddr.data_addr, HANDSHAKE_REG(ap->ioaddr.cmd_addr), le16_to_cpu(align_buf[0]));
		} else {
			align_buf[0] = cpu_to_le16(read_word_handshake(ap->ioaddr.data_addr, HANDSHAKE_REG(ap->ioaddr.cmd_addr)));
			memcpy(trailing_buf, align_buf, 1);
		}
	}
}

static void sata_pnx833x_bmdma_setup(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct sata_pnx833x_port_private_data *pd =
		(struct sata_pnx833x_port_private_data *) (ap->private_data);
	void __iomem *base = ap->ioaddr.cmd_addr;
	unsigned long dmactl;

	/* When this is called, buffer descriptor table is already
	 * created at ap->prd */

	pd->vpt[0] = ap->prd_dma;	/* this completes DMA structrures */
	wmb();						/* make sure all DMA structured are
								flushed to memory */
	dmactl = DMA_CTRL_START | DMA_PROTOCOL_ATA;
	if (qc->tf.flags & ATA_TFLAG_WRITE)
		dmactl |= DMA_DIRECTION_WRITE;
			 
	/* prepare DMA so hardware will start it when needed */
	writel(pd->vpt_dma, DMA_VP_REG(base));
	writel(dmactl, DMA_CTL_REG(base));

	/* now start i/o; ata_bmdma_setup() does it, so we also will */
	ap->ops->exec_command(ap, &qc->tf);
}

static void sata_pnx833x_bmdma_start(struct ata_queued_cmd *qc)
{
	/* Nothing needed to do here, because NHP hardware starts DMA
	 * without additional host actions. */
}

static u8 sata_pnx833x_bmdma_status(struct ata_port *ap)
{
	/* We try to emulate what libata expects, so we return ATA_DMA_INTR
	 * if DMA was complete and interrupt was fired because of that,
	 * and zero otherwise */
	void __iomem *base = ap->ioaddr.cmd_addr;
	return readl(DMA_STS_REG(base)) & 1 ? ATA_DMA_INTR : 0;
}

static void sata_pnx833x_bmdma_stop(struct ata_queued_cmd *qc)
{
	/* Assert DMA reset bit - per documentation, this is required on error
	 * path, and I guess it will not harm at normal path */
	struct ata_port *ap = qc->ap;
	void __iomem *base = ap->ioaddr.cmd_addr;
	writel(DMA_RESET, DMA_CTL_REG(base));
}

static irqreturn_t sata_pnx833x_interrupt(int irq, void *param)
{
	struct ata_host *host = (struct ata_host *)param;
	struct ata_port *ap = host->ports[0];
	void __iomem *base = ap->ioaddr.cmd_addr;
	unsigned long status, to_disable, to_clear, sata_err_to_clear;
	
	status = readl(INT_STS_REG(base)) & WANTED_INTERRUPTS;
	if (!status)
		return IRQ_NONE;

	to_disable = to_clear = sata_err_to_clear = 0;

	/* In case of situation that should bever happen, print an error
	 * message and then disable corresponding bit */

	if (status & DMA_ERROR_INT) {
		printk(KERN_ERR DRV_NAME ": UNEXPECTED: DMA not ready interrupt\n");
		to_disable |= DMA_ERROR_INT;
		to_clear |= DMA_ERROR_INT;
	}

	if (status & BAD_DMA_CMD_INT) {
		printk(KERN_ERR DRV_NAME ": UNEXPECTED: DMA invalid mode interrupt\n");
		to_disable |= BAD_DMA_CMD_INT;
		to_clear |= BAD_DMA_CMD_INT;
	}

	if (status & NO_DEV_INT1) {
		printk(KERN_ERR DRV_NAME ": UNEXPECTED: dev1 usage interrupt\n");
		to_disable |= NO_DEV_INT1;
		to_clear |= NO_DEV_INT1;
	}

	/* In case of different I/O errors, print an error message, clear
	 * interrupt and call libata handler */

	if (status & SERROR_E_INT) {
		printk(KERN_ERR DRV_NAME ": RX FIFO overflow\n");
		to_clear |= SERROR_E_INT;
		sata_err_to_clear |= SATA_ERR_SATA_ERR_E;
	}

	if (status & SERROR_F_INT) {
		printk(KERN_ERR DRV_NAME ": illegal FIS recieved\n");
		to_clear |= SERROR_F_INT;
		sata_err_to_clear |= SATA_ERR_SATA_DIAG_F;
	}

	if (status & SERROR_P_INT) {
		printk(KERN_ERR DRV_NAME ": recieved FIS not expected by protocol\n");
		to_clear |= SERROR_P_INT;
		sata_err_to_clear |= SATA_ERR_SATA_ERR_P;
	}

	if (status & SERROR_D_INT) {
		printk(KERN_ERR DRV_NAME ": disparity or decoding error\n");
		to_clear |= SERROR_D_INT;
		sata_err_to_clear |= SATA_ERR_SATA_DIAG_B;
	}

	if (status & SERROR_C_INT) {
		printk(KERN_ERR DRV_NAME ": CRC mismatch\n");
		to_clear |= SERROR_C_INT;
		sata_err_to_clear |= SATA_ERR_SATA_ERR_C;
	}

	if (status & SERROR_H_INT) {
		printk(KERN_ERR DRV_NAME ": handshake error\n");
		to_clear |= SERROR_H_INT;
		sata_err_to_clear |= SATA_ERR_SATA_DIAG_H;
	}

	if (status & DEVICE_INT_INT) {
		/* Clear it immediately, because libata won't call interrupt
		   clear callback on disabled port, while interrupt may happen
		   there */
		to_clear |= DEVICE_INT_INT;
	}

	if (status & DMA0_EOT_INT) {
		/* Clear it immediately, because libata won't call interrupt
		   clear callback on disabled port, while interrupt may happen
		   there */
		to_clear |= DMA0_EOT_INT;
	}

	/* clear and disable what desired, then maybe call libata handler */
	if (to_clear)
		writel(to_clear, INT_CLR_REG(base));
	if (sata_err_to_clear)
		writel(sata_err_to_clear, SATA_ERROR_REG(base));
	if (to_disable) {
		unsigned long val = readl(INT_ENA_REG(base));
		writel(val & ~to_disable, INT_ENA_REG(base));
	}

	if (status & (DEVICE_INT_INT | ALL_ERROR_INTERRUPTS))
		ata_interrupt(irq, param);
	return IRQ_HANDLED;
}

static void sata_pnx833x_irq_clear(struct ata_port *ap)
{
}

static inline void __iomem *scr_reg_addr(struct ata_port *ap, unsigned int reg)
{
	void __iomem *base = ap->ioaddr.cmd_addr;

	/* 0 - STATUS, 1 - ERROR, 2 - CONTROL, 3 - ACTIVE, 4 - NOTIFICATION */
	switch (reg) {
		case 0:
			return SATA_STATUS_REG(base);
		case 1:
			return SATA_ERROR_REG(base);
		case 2:
			return SATA_CONTROL_REG(base);
		case 3:
			return SATA_ACTIVE_REG(base);
		case 4:
			return SATA_NOTIFY_REG(base);
		default:
			printk(KERN_ERR DRV_NAME ": invalid SATA register "
					         "index %d\n", reg);
			return 0;
	}
}

static int sata_pnx833x_scr_read(struct ata_port *ap, unsigned int reg, u32 *val)
{
	void __iomem *reg_addr;

	reg_addr = scr_reg_addr(ap, reg);
	*val = reg_addr ? readl(reg_addr) : 0;
	return 0;
}

static int sata_pnx833x_scr_write(struct ata_port *ap, unsigned int reg, u32 val)
{
	void __iomem *reg_addr;

	reg_addr = scr_reg_addr(ap, reg);
	if (reg_addr) {
		/* Always write 1 to bit 4 of control reg, or hardware
		   won't work */
		if (reg == 2)
			val |= 0x10;

		writel(val, reg_addr);
	}
	return 0;
}

static unsigned int sata_pnx833x_qc_issue(struct ata_queued_cmd *qc)
{
	switch (qc->tf.protocol) {
		case ATA_PROT_NODATA:
		case ATA_PROT_DMA:
		case ATA_PROT_PIO:
			return ata_qc_issue_prot(qc);
		default:
			printk(KERN_ERR DRV_NAME ": unsupported qc protocol %d\n",
					qc->tf.protocol);
			WARN_ON(1);
			return AC_ERR_SYSTEM;
	}
}

static int sata_pnx833x_pre_reset(struct ata_link *link, unsigned long deadline)
{
	struct ata_port *ap = link->ap;
	ap->cbl = ATA_CBL_SATA;
	return ata_std_prereset(link, deadline);
}

static void generic_error_handler(struct ata_port *ap)
{
	ata_do_eh(ap, sata_pnx833x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
}
	
static struct scsi_host_template sata_pnx833x_sht = {
	.module				= THIS_MODULE,
	.name				= DRV_NAME,
	.ioctl				= ata_scsi_ioctl,
	.queuecommand		= ata_scsi_queuecmd,
	.can_queue			= ATA_DEF_QUEUE,
	.this_id			= ATA_SHT_THIS_ID,
	.sg_tablesize		= SATA_PNX833X_SG_SIZE,
	.max_sectors		= ATA_MAX_SECTORS,
	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
	.emulated			= ATA_SHT_EMULATED,
	.use_clustering		= ATA_SHT_USE_CLUSTERING,
	.proc_name			= DRV_NAME,
	.dma_boundary		= 0xefff,
	.slave_configure	= ata_scsi_slave_config,
	.bios_param			= ata_std_bios_param,
};

static struct ata_port_operations sata_pnx833x_port_ops = {
	.tf_load			= sata_pnx833x_tf_load,
	.tf_read			= ata_tf_read,
	.check_status 		= ata_check_status,
	.exec_command		= ata_exec_command,
	.dev_select 		= ata_std_dev_select,
	.bmdma_setup 		= sata_pnx833x_bmdma_setup,
	.bmdma_start 		= sata_pnx833x_bmdma_start,
	.bmdma_stop			= sata_pnx833x_bmdma_stop,
	.bmdma_status 		= sata_pnx833x_bmdma_status,
	.data_xfer			= sata_pnx833x_data_xfer,
	.freeze				= ata_bmdma_freeze,
	.thaw				= ata_bmdma_thaw,
	.error_handler		= generic_error_handler,
	.post_internal_cmd 	= sata_pnx833x_bmdma_stop,
	.qc_prep 			= ata_qc_prep,
	.qc_issue			= sata_pnx833x_qc_issue,
	.irq_handler		= sata_pnx833x_interrupt,
	.irq_clear			= sata_pnx833x_irq_clear,
	.irq_on				= ata_irq_on,
	.scr_read			= sata_pnx833x_scr_read,
	.scr_write			= sata_pnx833x_scr_write,
	.port_start			= sata_pnx833x_port_start,
};

static void sata_pnx833x_setup_port(struct ata_ioports *ioaddr, unsigned int shift)
{
	ioaddr->data_addr      = ioaddr->cmd_addr + (ATA_REG_DATA    << shift);
	ioaddr->error_addr     = ioaddr->cmd_addr + (ATA_REG_ERR     << shift);
	ioaddr->feature_addr   = ioaddr->cmd_addr + (ATA_REG_FEATURE << shift);
	ioaddr->nsect_addr     = ioaddr->cmd_addr + (ATA_REG_NSECT   << shift);
	ioaddr->lbal_addr      = ioaddr->cmd_addr + (ATA_REG_LBAL    << shift);
	ioaddr->lbam_addr      = ioaddr->cmd_addr + (ATA_REG_LBAM    << shift);
	ioaddr->lbah_addr      = ioaddr->cmd_addr + (ATA_REG_LBAH    << shift);
	ioaddr->device_addr    = ioaddr->cmd_addr + (ATA_REG_DEVICE  << shift);
	ioaddr->status_addr    = ioaddr->cmd_addr + (ATA_REG_STATUS  << shift);
	ioaddr->command_addr   = ioaddr->cmd_addr + (ATA_REG_CMD     << shift);
	ioaddr->altstatus_addr = ioaddr->cmd_addr + (8               << shift);
	ioaddr->ctl_addr       = ioaddr->cmd_addr + (8               << shift);
	ioaddr->bmdma_addr     = ioaddr->cmd_addr + (12              << shift);
	ioaddr->scr_addr	   = ioaddr->data_addr;
}

static int sata_pnx833x_probe(struct platform_device *pdev)
{
	struct ata_host *host;
	struct ata_port *ap;
	struct resource *res;
	int              irq;

	host = ata_host_alloc(&pdev->dev, 1);
	if (!host)
	{
		return -ENOMEM;
	}

	ap = host->ports[0];

	/* find the platform resources */
	irq  = platform_get_irq(pdev, 0);
	if (irq < 0)
	{
		dev_err(&pdev->dev, "no IRQ specified\n");
		return -ENXIO;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL)
	{
		dev_err(&pdev->dev, "no MEM specified\n");
		return -ENXIO;
	}

	ap->ops        = &sata_pnx833x_port_ops;
	ap->pio_mask   = 0x1f;        /* pio0-4 */
	ap->mwdma_mask = 0x7;         /* mwdma0-2 */
	ap->udma_mask  = 0x7f;        /* udma0-6 */
	ap->flags     |= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET | ATA_FLAG_SRST;

	ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, res->start & 0x1fffffff, (res->end - res->start) + 1);
	sata_pnx833x_setup_port(&ap->ioaddr, 2);

	/* reset the hw as far as we can */
	writel(0x00000000, INT_ENA_REG(ap->ioaddr.cmd_addr));
	writel(1,          SWRESET_REG(ap->ioaddr.cmd_addr));
	writel(0xffffffff, INT_CLR_REG(ap->ioaddr.cmd_addr));

	return ata_host_activate(host, irq, sata_pnx833x_interrupt, 0, &sata_pnx833x_sht);
}


static int sata_pnx833x_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct ata_host *host = dev_get_drvdata(dev);

	ata_host_detach(host);

	return 0;
}

#ifdef CONFIG_PM
static int sata_pnx833x_suspend(struct platform_device *pdev, pm_message_t state)
{
	return 0;
}

static int sata_pnx833x_resume(struct platform_device *pdev)
{
	return 0;
}
#endif

static struct platform_driver sata_pnx833x_driver = {
	.driver	= {
		.name		= DRV_NAME,
		.owner		= THIS_MODULE,
	},
	.probe		= sata_pnx833x_probe,
	.remove		= sata_pnx833x_remove,
#ifdef CONFIG_PM
	.suspend	= sata_pnx833x_suspend,
	.resume		= sata_pnx833x_resume,
#endif
};

static int __init sata_pnx833x_init(void)
{
	return platform_driver_register(&sata_pnx833x_driver);
}

static void __exit sata_pnx833x_exit(void)
{
	platform_driver_unregister(&sata_pnx833x_driver);
}

module_init(sata_pnx833x_init);
module_exit(sata_pnx833x_exit);

MODULE_DESCRIPTION("NXP pnx833x sata platform driver");
MODULE_AUTHOR("Chris Steel, <chris.steel@nxp.com>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);

