From 8e46c06091fd87904205a977be3c784e3ac61e95 Mon Sep 17 00:00:00 2001
From: Jingchang Lu <b35083@freescale.com>
Date: Thu, 4 Aug 2011 09:59:48 +0800
Subject: [PATCH 37/52] Add ColdFire MCF54455 PATA interface support

ColdFire MCF54455 parallel ATA controller support
both uDMA and PIO mode, this driver implements all.

Signed-off-by: Jingchang Lu <b35083@freescale.com>
---
 arch/m68k/include/asm/pata_fsl.h |   17 +
 drivers/ata/Kconfig              |   23 +-
 drivers/ata/Makefile             |    1 +
 drivers/ata/pata_fsl.c           |  844 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 884 insertions(+), 1 deletions(-)
 create mode 100644 arch/m68k/include/asm/pata_fsl.h
 create mode 100644 drivers/ata/pata_fsl.c

--- /dev/null
+++ b/arch/m68k/include/asm/pata_fsl.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_M68K_PATA_FSL_H
+#define _ASM_M68K_PATA_FSL_H
+
+/* ATA mapped IO address translate function */
+extern unsigned int io_ata_virt2phys(void *x);
+extern void *io_ata_phys2virt(unsigned int x);
+
+
+#endif
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -14,7 +14,7 @@ menuconfig ATA
 	tristate "Serial ATA and Parallel ATA drivers"
 	depends on HAS_IOMEM
 	depends on BLOCK
-	depends on !(M32R || M68K) || BROKEN
+	depends on !(M32R) || BROKEN
 	select SCSI
 	---help---
 	  If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or
@@ -687,6 +687,27 @@ config PATA_WINBOND
 
 	  If unsure, say N.
 
+config PATA_FSL
+        tristate "Freescale on-chip PATA support"
+        depends on (ARCH_MX3 || ARCH_MX27 || PPC_512x || M54455)
+        help
+          Some Freescale processors SOC have parallel ATA controller,
+	  such as ColdFire MCF54455.
+
+	  Say Y here if you wish to use the on-chip ATA interface.
+
+	  If you are unsure, say N to this.
+
+config FSL_PATA_USE_DMA
+	bool "Freescale PATA eDMA support"
+	depends on PATA_FSL && COLDFIRE_EDMA
+	default y
+	help
+	  This option enables the uDMA support over PATA interface
+	  which can improve performance than PIO mode for read and write.
+
+	  If unsure, say Y.
+
 endif # ATA_BMDMA
 
 comment "PIO-only SFF controllers"
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_PATA_TOSHIBA)	+= pata_picco
 obj-$(CONFIG_PATA_TRIFLEX)	+= pata_triflex.o
 obj-$(CONFIG_PATA_VIA)		+= pata_via.o
 obj-$(CONFIG_PATA_WINBOND)	+= pata_sl82c105.o
+obj-$(CONFIG_PATA_FSL)          += pata_fsl.o
 
 # SFF PIO only
 obj-$(CONFIG_PATA_AT32)		+= pata_at32.o
--- /dev/null
+++ b/drivers/ata/pata_fsl.c
@@ -0,0 +1,844 @@
+/*
+ * Freescale integrated PATA driver
+ *
+ * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Description:
+ * This driver is for Coldfire MCF54455 on-chip ATA module.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the GNU General Public License  as published by the  Free
+ * Software Foundation; either  version 2 of the License,  or  (at
+ * your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#ifdef CONFIG_FSL_PATA_USE_DMA
+#include <asm/mcf_edma.h>
+#endif
+#include <asm/pata_fsl.h>
+
+#define DRV_NAME "pata_fsl"
+#define DRV_VERSION "1.0"
+
+#ifdef CONFIG_M54455
+#define WRITE_ATA8(val, reg)			\
+	__raw_writeb(val, (ata_regs + reg));
+#define WRITE_ATA16(val, reg)			\
+	__raw_writew(val, (ata_regs + reg));
+#else
+#define WRITE_ATA8(val, reg)			\
+	__raw_writel(val, (ata_regs + reg));
+#define WRITE_ATA16(val, reg)			\
+	__raw_writel(val, (ata_regs + reg));
+#endif
+
+#define MAX_FSL_SG 256	/* MCF_EDMA_TCD_PER_CHAN */
+
+struct pata_fsl_priv {
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	int ultra;
+#endif
+	u8 *fsl_ata_regs;
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	int dma_rchan;
+	int dma_wchan;
+	int dma_done;
+	int dma_dir;
+#if 0
+	int nsg;
+	struct fsl_edma_requestbuf reqbuf[MAX_FSL_SG];
+#endif
+#endif
+};
+
+enum {
+	/* various constants */
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	FSL_ATA_MAX_SG_LEN      = 65534,
+#endif
+
+	/* offsets to registers */
+
+	FSL_ATA_TIMING_REGS     = 0x00,
+	FSL_ATA_FIFO_FILL       = 0x20,
+	FSL_ATA_CONTROL         = 0x24,
+	FSL_ATA_INT_PEND        = 0x28,
+	FSL_ATA_INT_EN          = 0x2C,
+	FSL_ATA_INT_CLEAR       = 0x30,
+	FSL_ATA_FIFO_ALARM      = 0x34,
+	FSL_ATA_DRIVE_DATA      = 0xA0,
+	FSL_ATA_DRIVE_CONTROL   = 0xD8,
+
+	/* bits within FSL_ATA_CONTROL */
+
+	FSL_ATA_CTRL_FIFO_RST_B    = 0x80,
+	FSL_ATA_CTRL_ATA_RST_B     = 0x40,
+	FSL_ATA_CTRL_FIFO_TX_EN    = 0x20,
+	FSL_ATA_CTRL_FIFO_RCV_EN   = 0x10,
+	FSL_ATA_CTRL_DMA_PENDING   = 0x08,
+	FSL_ATA_CTRL_DMA_ULTRA     = 0x04,
+	FSL_ATA_CTRL_DMA_WRITE     = 0x02,
+	FSL_ATA_CTRL_IORDY_EN      = 0x01,
+
+	/* bits within the interrupt control registers */
+
+	FSL_ATA_INTR_ATA_INTRQ1      = 0x80,
+	FSL_ATA_INTR_FIFO_UNDERFLOW  = 0x40,
+	FSL_ATA_INTR_FIFO_OVERFLOW   = 0x20,
+	FSL_ATA_INTR_CTRL_IDLE       = 0x10,
+	FSL_ATA_INTR_ATA_INTRQ2      = 0x08,
+};
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 5 PIO modes.  The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+	short t0, t1, t2_8, t2_16, t2i, t4, t9, tA;
+} pio_specs[] = {
+	[0] = {
+		.t0 = 600, .t1 = 70, .t2_8 = 290, .t2_16 = 165, .t2i = 0,
+		.t4 = 30, .t9 = 20, .tA = 50
+	},
+	[1] = {
+		.t0 = 383, .t1 = 50, .t2_8 = 290, .t2_16 = 125, .t2i = 0,
+		.t4 = 20, .t9 = 15, .tA = 50
+	},
+	[2] = {
+		.t0 = 240, .t1 = 30, .t2_8 = 290, .t2_16 = 100, .t2i = 0,
+		.t4 = 15, .t9 = 10, .tA = 50
+	},
+	[3] = {
+		.t0 = 180, .t1 = 30, .t2_8 = 80, .t2_16 = 80, .t2i = 0,
+		.t4 = 10, .t9 = 10, .tA = 50
+	},
+	[4] = {
+		.t0 = 120, .t1 = 25, .t2_8 = 70, .t2_16 = 70, .t2i = 0,
+		.t4 = 10, .t9 = 10, .tA = 50
+	},
+};
+
+#define NR_PIO_SPECS (sizeof pio_specs / sizeof pio_specs[0])
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 3 MDMA modes.  The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+	short t0M, tD, tH, tJ, tKW, tM, tN, tJNH;
+} mdma_specs[] = {
+	[0] = {
+		.t0M = 480, .tD = 215, .tH = 20, .tJ = 20, .tKW = 215,
+		.tM = 50, .tN = 15, .tJNH = 20
+	},
+	[1] = {
+		.t0M = 150, .tD = 80, .tH = 15, .tJ = 5, .tKW = 50,
+		.tM = 30, .tN = 10, .tJNH = 15
+	},
+	[2] = {
+		.t0M = 120, .tD = 70, .tH = 10, .tJ = 5, .tKW = 25,
+		.tM = 25, .tN = 10, .tJNH = 10
+	},
+};
+
+#define NR_MDMA_SPECS (sizeof mdma_specs / sizeof mdma_specs[0])
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 6 UDMA modes.  The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+	short t2CYC, tCYC, tDS, tDH, tDVS, tDVH, tCVS, tCVH, tFS_min, tLI_max,
+	    tMLI, tAZ, tZAH, tENV_min, tSR, tRFS, tRP, tACK, tSS, tDZFS;
+} udma_specs[] = {
+	[0] = {
+		.t2CYC = 235, .tCYC = 114, .tDS = 15, .tDH = 5, .tDVS = 70,
+		.tDVH = 6, .tCVS = 70, .tCVH = 6, .tFS_min = 0,
+		.tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 50, .tRFS = 75, .tRP = 160,
+		.tACK = 20, .tSS = 50, .tDZFS = 80
+	},
+	[1] = {
+		.t2CYC = 156, .tCYC = 75, .tDS = 10, .tDH = 5, .tDVS = 48,
+		.tDVH = 6, .tCVS = 48, .tCVH = 6, .tFS_min = 0,
+		.tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 30, .tRFS = 70, .tRP = 125,
+		.tACK = 20, .tSS = 50, .tDZFS = 63
+	},
+	[2] = {
+		.t2CYC = 117, .tCYC = 55, .tDS = 7, .tDH = 5, .tDVS = 34,
+		.tDVH = 6, .tCVS = 34, .tCVH = 6, .tFS_min = 0,
+		.tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 20, .tRFS = 60, .tRP = 100,
+		.tACK = 20, .tSS = 50, .tDZFS = 47
+	},
+	[3] = {
+		.t2CYC = 86, .tCYC = 39, .tDS = 7, .tDH = 5, .tDVS = 20,
+		.tDVH = 6, .tCVS = 20, .tCVH = 6, .tFS_min = 0,
+		.tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 20, .tRFS = 60, .tRP = 100,
+		.tACK = 20, .tSS = 50, .tDZFS = 35
+	},
+	[4] = {
+		.t2CYC = 57, .tCYC = 25, .tDS = 5, .tDH = 5, .tDVS = 7,
+		.tDVH = 6, .tCVS = 7, .tCVH = 6, .tFS_min = 0,
+		.tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 50, .tRFS = 60, .tRP = 100,
+		.tACK = 20, .tSS = 50, .tDZFS = 25
+	},
+	[5] = {
+		.t2CYC = 38, .tCYC = 17, .tDS = 4, .tDH = 5, .tDVS = 5,
+		.tDVH = 6, .tCVS = 10, .tCVH = 10, .tFS_min = 0,
+		.tLI_max = 75, .tMLI = 20, .tAZ = 10, .tZAH = 20,
+		.tENV_min = 20, .tSR = 20, .tRFS = 50, .tRP = 85,
+		.tACK = 20, .tSS = 50, .tDZFS = 40
+	},
+};
+
+#define NR_UDMA_SPECS (sizeof udma_specs / sizeof udma_specs[0])
+
+struct fsl_ata_time_regs {
+	u8 time_off, time_on, time_1, time_2w;
+	u8 time_2r, time_ax, time_pio_rdx, time_4;
+	u8 time_9, time_m, time_jn, time_d;
+	u8 time_k, time_ack, time_env, time_rpx;
+	u8 time_zah, time_mlix, time_dvh, time_dzfs;
+	u8 time_dvs, time_cvh, time_ss, time_cyc;
+} __packed;
+
+
+static void update_timing_config(struct fsl_ata_time_regs *tp,
+			struct ata_host *host)
+{
+	u32 __iomem *lp = (u32 __iomem *)tp;
+	struct pata_fsl_priv *priv = host->private_data;
+	u32 __iomem *ctlp = (u32 __iomem *)priv->fsl_ata_regs;
+	int i;
+
+	/*
+	 * JKM - this could have endianess issues on BE depending
+	 * on how the controller is glued to the bus -- probably
+	 * should rewrite this to write byte at a time.
+	 */
+	for (i = 0; i < 6; i++) {
+		__raw_writel(*lp, ctlp);
+		lp++;
+		ctlp++;
+	}
+}
+
+/*!
+ * Calculate values for the ATA bus timing registers and store
+ * them into the hardware.
+ *
+ * @param       xfer_mode   specifies XFER xfer_mode
+ * @param       pdev        specifies platform_device
+ *
+ * @return      EINVAL      speed out of range, or illegal mode
+ */
+static int set_ata_bus_timing(u8 xfer_mode, struct platform_device *pdev)
+{
+	struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+					     pdev->dev.platform_data;
+	struct ata_host *host = dev_get_drvdata(&pdev->dev);
+
+	/* get the bus clock cycle time, in ns */
+	int T = 1 * 1000 * 1000 * 1000 / plat->get_clk_rate();
+	struct fsl_ata_time_regs tr = {0};
+	DPRINTK("clk_rate = %d  T = %d\n", plat->get_clk_rate(), T);
+
+	/*
+	 * every mode gets the same t_off and t_on
+	 */
+	tr.time_off = 3;
+	tr.time_on = 3;
+
+	if (xfer_mode >= XFER_UDMA_0) {
+		int speed = xfer_mode - XFER_UDMA_0;
+		if (speed >= NR_UDMA_SPECS)
+			return -EINVAL;
+
+		tr.time_ack = (udma_specs[speed].tACK + T) / T;
+		tr.time_env = (udma_specs[speed].tENV_min + T) / T;
+		tr.time_rpx = (udma_specs[speed].tRP + T) / T + 2;
+
+		tr.time_zah = (udma_specs[speed].tZAH + T) / T;
+		tr.time_mlix = (udma_specs[speed].tMLI + T) / T;
+		tr.time_dvh = (udma_specs[speed].tDVH + T) / T + 1;
+		tr.time_dzfs = (udma_specs[speed].tDZFS + T) / T;
+
+		tr.time_dvs = (udma_specs[speed].tDVS + T) / T;
+		tr.time_cvh = (udma_specs[speed].tCVH + T) / T;
+		tr.time_ss = (udma_specs[speed].tSS + T) / T;
+		tr.time_cyc = (udma_specs[speed].tCYC + T) / T;
+	} else if (xfer_mode >= XFER_MW_DMA_0) {
+		int speed = xfer_mode - XFER_MW_DMA_0;
+		if (speed >= NR_MDMA_SPECS)
+			return -EINVAL;
+
+		tr.time_m = (mdma_specs[speed].tM + T) / T;
+		tr.time_jn = (mdma_specs[speed].tJNH + T) / T;
+		tr.time_d = (mdma_specs[speed].tD + T) / T;
+
+		tr.time_k = (mdma_specs[speed].tKW + T) / T;
+	} else {
+		int speed = xfer_mode - XFER_PIO_0;
+		if (speed >= NR_PIO_SPECS)
+			return -EINVAL;
+
+		tr.time_1 = (pio_specs[speed].t1 + T) / T;
+		tr.time_2w = (pio_specs[speed].t2_8 + T) / T;
+
+		tr.time_2r = (pio_specs[speed].t2_8 + T) / T;
+		tr.time_ax = (pio_specs[speed].tA + T) / T + 2;
+		tr.time_pio_rdx = 1;
+		tr.time_4 = (pio_specs[speed].t4 + T) / T;
+
+		tr.time_9 = (pio_specs[speed].t9 + T) / T;
+	}
+
+	update_timing_config(&tr, host);
+
+	return 0;
+}
+
+static void pata_fsl_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+	set_ata_bus_timing(adev->pio_mode, to_platform_device(ap->dev));
+}
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+static void pata_fsl_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct pata_fsl_priv *priv = ap->host->private_data;
+
+	priv->ultra = adev->dma_mode >= XFER_UDMA_0;
+
+	set_ata_bus_timing(adev->dma_mode, to_platform_device(ap->dev));
+}
+#endif
+
+static int pata_fsl_port_start(struct ata_port *ap)
+{
+	return 0;
+}
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+
+static irqreturn_t dma_callback(int channel, void *arg)
+{
+	struct ata_port *ap = arg;
+	struct pata_fsl_priv *priv = ap->host->private_data;
+	u8 __iomem *ata_regs = priv->fsl_ata_regs;
+
+		mcf_edma_stop_transfer(channel);
+		priv->dma_done = 1;
+	/*
+	 * DMA is finished, so unmask INTRQ from the drive to allow the
+	 * normal ISR to fire.
+	 */
+#if 0
+	__raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
+#else
+	WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN);
+	WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void pata_fsl_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	int chan;
+	int dma_ultra;
+	u8 ata_control;
+	struct ata_port *ap = qc->ap;
+	struct pata_fsl_priv *priv = ap->host->private_data;
+	u8 __iomem *ata_regs = priv->fsl_ata_regs;
+#if 0
+	struct scatterlist  *sg;
+	struct fsl_edma_requestbuf *pbuf;
+	unsigned int si;
+#endif
+	DPRINTK("ENTER\n");
+
+	/* reset the ATA FIFO first */
+	/*
+	WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B,FSL_ATA_CONTROL);
+	*/
+	priv->dma_dir = qc->dma_dir;
+
+	/*
+	 * Configure the on-chip ATA interface hardware.
+	 */
+	dma_ultra = priv->ultra ?
+		FSL_ATA_CTRL_DMA_ULTRA : 0;
+
+	ata_control = FSL_ATA_CTRL_FIFO_RST_B |
+		      FSL_ATA_CTRL_ATA_RST_B |
+		      FSL_ATA_CTRL_DMA_PENDING |
+		      dma_ultra;
+
+	if (qc->dma_dir == DMA_TO_DEVICE) {
+		chan = priv->dma_wchan;
+		ata_control |= FSL_ATA_CTRL_FIFO_TX_EN |
+			      FSL_ATA_CTRL_DMA_WRITE;
+	} else {
+		chan = priv->dma_rchan;
+		ata_control |= FSL_ATA_CTRL_FIFO_RCV_EN;
+	}
+#if 0
+	__raw_writel(ata_control, ata_regs + FSL_ATA_CONTROL);
+	__raw_writel(plat->fifo_alarm, ata_regs + FSL_ATA_FIFO_ALARM);
+	__raw_writel(FSL_ATA_INTR_ATA_INTRQ1, ata_regs + FSL_ATA_INT_EN);
+#else
+	WRITE_ATA8(ata_control, FSL_ATA_CONTROL);
+	WRITE_ATA8(16/*plat->fifo_alarm*/, FSL_ATA_FIFO_ALARM);
+	WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ1, FSL_ATA_INT_EN);
+#endif
+	/*mb();*/
+
+	/*
+	 * Set up the DMA completion callback.
+	 */
+	/*
+	 * Copy the sg list to an array.
+	 */
+#if 0
+	priv->nsg = 0;
+	pbuf = priv->reqbuf;
+
+	for_each_sg(qc->sg, sg, qc->n_elem, si) {
+
+	/*dma_map_sg(NULL, sg, 1, priv->dma_dir);	*/
+
+		if (priv->dma_dir == DMA_TO_DEVICE) {	/* WRITE */
+			pbuf->saddr = sg->dma_address;
+			pbuf->daddr = (dma_addr_t)(priv->fsl_ata_regs + 0x18);
+			pbuf->soff = 4;
+			pbuf->doff = 0;
+		} else {					/* Read */
+			pbuf->daddr = sg->dma_address;
+			pbuf->saddr = (dma_addr_t)(priv->fsl_ata_regs + 0x18);
+			pbuf->doff = 4;
+			pbuf->soff = 0;
+		}
+		pbuf->attr = MCF_EDMA_TCD_ATTR_SSIZE_32BIT
+			|MCF_EDMA_TCD_ATTR_DSIZE_32BIT;
+		pbuf->minor_loop = 16*4;	/* 16 longwords per request*/
+		pbuf->len = sg_dma_len(sg);
+
+		pbuf++;
+		priv->nsg++;
+	}
+
+	BUG_ON(*(unsigned char *)(ata_regs + FSL_ATA_FIFO_FILL));
+	mcf_edma_sg_config(chan, priv->reqbuf, priv->nsg);
+#else
+	if (priv->dma_dir == DMA_TO_DEVICE) {
+		mcf_edma_sglist_config(chan, qc->sg, qc->n_elem, priv->dma_dir,
+			(dma_addr_t)
+			((io_ata_virt2phys((void *)priv->fsl_ata_regs)) + 0x18),
+			MCF_EDMA_TCD_ATTR_SSIZE_32BIT
+			| MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
+			4, 0, 8*4);
+	} else {
+
+		mcf_edma_sglist_config(chan, qc->sg, qc->n_elem, priv->dma_dir,
+			(dma_addr_t)
+			((io_ata_virt2phys((void *)priv->fsl_ata_regs)) + 0x18),
+			MCF_EDMA_TCD_ATTR_SSIZE_32BIT
+			| MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
+			0, 4, 8*4);
+	}
+
+#endif
+	priv->dma_done = 0;
+
+	DPRINTK("EXIT\n");
+
+}
+
+static void pata_fsl_bmdma_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_fsl_priv *priv = ap->host->private_data;
+	int chan;
+
+	/*
+	 * Start the channel.
+	 */
+	chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan : priv->dma_rchan;
+
+	mcf_edma_enable_transfer(chan);
+
+	ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void pata_fsl_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+/*
+	int chan;
+
+	chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan : priv->dma_rchan;
+	mcf_edma_stop_transfer(chan);
+*/
+/* do a dummy read as in ata_bmdma_stop */
+	ata_sff_dma_pause(ap);
+}
+
+static u8 pata_fsl_bmdma_status(struct ata_port *ap)
+{
+	struct pata_fsl_priv *priv = ap->host->private_data;
+
+	return priv->dma_done ? ATA_DMA_INTR : 0;
+}
+
+static void pata_fsl_dma_init(struct ata_port *ap)
+{
+	struct pata_fsl_priv *priv = ap->host->private_data;
+
+	priv->dma_rchan = -1;
+	priv->dma_wchan = -1;
+
+	priv->dma_rchan = mcf_edma_request_channel(MCF_EDMA_CHAN_ATA_RX,
+				dma_callback,
+				NULL, 0x6,
+				(void *)ap,
+				NULL,
+				"MCF ATA RX");
+	if (priv->dma_rchan < 0) {
+		dev_printk(KERN_ERR, ap->dev, "couldn't get RX DMA channel\n");
+		goto err_out;
+	}
+
+	priv->dma_wchan = mcf_edma_request_channel(MCF_EDMA_CHAN_ATA_TX,
+				dma_callback,
+				NULL, 0x6,
+				(void *)ap,
+				NULL,
+				"MCF ATA TX");
+	if (priv->dma_wchan < 0) {
+		dev_printk(KERN_ERR, ap->dev, "couldn't get TX DMA channel\n");
+		goto err_out;
+	}
+
+	dev_printk(KERN_ERR, ap->dev, "rchan=%d wchan=%d\n", priv->dma_rchan,
+		   priv->dma_wchan);
+	return;
+
+err_out:
+	ap->mwdma_mask = 0;
+	ap->udma_mask = 0;
+	mcf_edma_free_channel(priv->dma_rchan, ap);
+	mcf_edma_free_channel(priv->dma_wchan, ap);
+	kfree(priv);
+}
+#endif /* CONFIG_FSL_PATA_USE_DMA */
+
+static void ata_dummy_noret(struct ata_port *ap) { return; }
+
+static struct scsi_host_template pata_fsl_sht = {
+	ATA_BMDMA_SHT(DRV_NAME),
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	.sg_tablesize		= MAX_FSL_SG,
+	.dma_boundary		= ATA_DMA_BOUNDARY,
+#endif
+};
+
+static struct ata_port_operations pata_fsl_port_ops = {
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	.inherits               = &ata_bmdma_port_ops,
+#else
+	.inherits               = &ata_sff_port_ops,
+#endif
+	.set_piomode		= pata_fsl_set_piomode,
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	.set_dmamode		= pata_fsl_set_dmamode,
+#endif
+	.cable_detect		= ata_cable_40wire,
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	.bmdma_setup		= pata_fsl_bmdma_setup,
+	.bmdma_start		= pata_fsl_bmdma_start,
+#endif
+
+	.sff_data_xfer		= ata_sff_data_xfer_noirq,
+	.qc_prep		= ata_noop_qc_prep,
+
+	.port_start		= pata_fsl_port_start,
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	.bmdma_stop		= pata_fsl_bmdma_stop,
+	.bmdma_status		= pata_fsl_bmdma_status,
+#endif
+};
+
+static void fsl_setup_port(struct ata_ioports *ioaddr)
+{
+	unsigned int shift = 2;
+
+	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);
+}
+
+/**
+ *	pata_fsl_probe		-	attach a platform interface
+ *	@pdev: platform device
+ *
+ *	Register a platform bus integrated ATA host controller
+ *
+ *	The 3 platform device resources are used as follows:
+ *
+ *		- I/O Base (IORESOURCE_MEM) virt. addr. of ATA controller regs
+ *		- CTL Base (IORESOURCE_MEM) unused
+ *		- IRQ	   (IORESOURCE_IRQ) platform IRQ assigned to ATA
+ *
+ */
+static int __devinit pata_fsl_probe(struct platform_device *pdev)
+{
+	struct resource *io_res;
+	struct ata_host *host;
+	struct ata_port *ap;
+	struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+					     pdev->dev.platform_data;
+	struct pata_fsl_priv *priv;
+	u8 *ata_regs;
+	int ret;
+
+	DPRINTK("ENTER\n");
+	/*
+	 * Get an ata_host structure for this device
+	 */
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		return -ENOMEM;
+	ap = host->ports[0];
+	/*
+	 * Allocate private data
+	 */
+	priv = kzalloc(sizeof(struct pata_fsl_priv), GFP_KERNEL);
+	if (priv == NULL) {
+		/* free(host); */
+		return -ENOMEM;
+	}
+	host->private_data = priv;
+
+	/*
+	 * Set up resources
+	 */
+	if (unlikely(pdev->num_resources != 3)) {
+		dev_err(&pdev->dev, "invalid number of resources\n");
+		return -EINVAL;
+	}
+
+	io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ata_regs = (u8 *)io_res->start;
+	priv->fsl_ata_regs = ata_regs;
+	ap->ioaddr.cmd_addr = (void *)(ata_regs + FSL_ATA_DRIVE_DATA);
+	ap->ioaddr.ctl_addr = (void *)(ata_regs + FSL_ATA_DRIVE_CONTROL);
+	ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+	ap->ops = &pata_fsl_port_ops;
+	ap->pio_mask = 0x3F;
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	ap->mwdma_mask = 0x07;
+	ap->udma_mask = 0x1F;
+/*	ap->udma_mask = plat->udma_mask;	*/
+/*	pata_fsl_sht.sg_tablesize = plat->max_sg;	*/
+#else
+	ap->mwdma_mask = 0x00;
+	ap->udma_mask = 0x00;
+#endif
+	fsl_setup_port(&ap->ioaddr);
+
+	/*
+	 * Do platform-specific initialization (e.g. allocate pins,
+	 * turn on clock).  After this call it is assumed that
+	 * plat->get_clk_rate() can be called to calculate
+	 * timing.
+	 */
+	if (plat->init && plat->init(pdev)) {
+		/* REVISIT: don't leak what ata_host_alloc() allocated */
+		return -ENODEV;
+	}
+
+	/* Deassert the reset bit to enable the interface */
+	WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL);
+
+	/* Set initial timing and mode */
+	set_ata_bus_timing(XFER_PIO_4, pdev);
+
+#ifdef CONFIG_FSL_PATA_USE_DMA
+	/* get DMA ready */
+	pata_fsl_dma_init(ap);
+#endif
+
+	/*
+	 * Enable the ATA INTRQ interrupt from the bus, but
+	 * only allow the CPU to see it (INTRQ2) at this point.
+	 * INTRQ1, which goes to the DMA, will be enabled later.
+	 */
+#if 0
+	__raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
+#else
+	WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN);
+#endif
+
+	/* activate */
+	ret = ata_host_activate(host, platform_get_irq(pdev, 0),
+				ata_sff_interrupt, 0, &pata_fsl_sht);
+	DPRINTK("EXIT ret=%d\n", ret);
+	return ret;
+}
+
+/**
+ *	pata_fsl_remove	-	unplug a platform interface
+ *	@pdev: platform device
+ *
+ *	A platform bus ATA device has been unplugged. Perform the needed
+ *	cleanup. Also called on module unload for any active devices.
+ */
+static int __devexit pata_fsl_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct pata_fsl_priv *priv = host->private_data;
+	struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+					     pdev->dev.platform_data;
+	u8 *ata_regs = priv->fsl_ata_regs;
+
+#if 0
+	__raw_writel(0, ata_regs + FSL_ATA_INT_EN);  /* Disable interrupts */
+#else
+	WRITE_ATA8(0, FSL_ATA_INT_EN); /* Disable interrupts */
+#endif
+
+	ata_host_detach(host);
+
+	if (plat->exit)
+		plat->exit();
+
+	kfree(priv);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pata_fsl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct ata_host *host = dev_get_drvdata(&pdev->dev);
+	struct pata_fsl_priv *priv = host->private_data;
+	struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+					     pdev->dev.platform_data;
+	u8 *ata_regs = priv->fsl_ata_regs;
+
+	/* Disable interrupts. */
+#if 0
+	__raw_writel(0, ata_regs + FSL_ATA_INT_EN);
+#else
+	WRITE_ATA8(0, FSL_ATA_INT_EN);
+#endif
+
+	if (plat->exit)
+		plat->exit();
+
+	return 0;
+}
+
+static int pata_fsl_resume(struct platform_device *pdev)
+{
+	struct ata_host *host = dev_get_drvdata(&pdev->dev);
+	struct pata_fsl_priv *priv = host->private_data;
+	struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+					     pdev->dev.platform_data;
+	u8 *ata_regs = priv->fsl_ata_regs;
+
+	if (plat->init && plat->init(pdev))
+		return -ENODEV;
+	/* Deassert the reset bit to enable the interface */
+#if 0
+	__raw_writel(FSL_ATA_CTRL_ATA_RST_B, ata_regs + FSL_ATA_CONTROL);
+#else
+	WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL);
+#endif
+
+	/* Set initial timing and mode */
+	set_ata_bus_timing(XFER_PIO_4, pdev);
+
+	/*
+	 * Enable hardware interrupts.
+	 */
+#if 0
+	__raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
+#else
+	WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN);
+#endif
+
+	return 0;
+}
+#endif
+
+static struct platform_driver pata_fsl_driver = {
+	.probe		= pata_fsl_probe,
+	.remove		= __devexit_p(pata_fsl_remove),
+#ifdef CONFIG_PM
+	.suspend	= pata_fsl_suspend,
+	.resume		= pata_fsl_resume,
+#endif
+	.driver = {
+		.name		= DRV_NAME,
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init pata_fsl_init(void)
+{
+	int ret;
+
+	DPRINTK("ENTER\n");
+	ret = platform_driver_register(&pata_fsl_driver);
+	DPRINTK("EXIT ret=%d\n", ret);
+	return ret;
+}
+
+static void __exit pata_fsl_exit(void)
+{
+	platform_driver_unregister(&pata_fsl_driver);
+}
+module_init(pata_fsl_init);
+module_exit(pata_fsl_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("low-level driver for Freescale ATA");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
