On Wed, Aug 13, 2008 at 7:11 AM, Grant Likely <[EMAIL PROTECTED]> wrote:
> Sounds good to me.  You will get more testers that way.  I can pick it
> up for -next if everything else looks good.

Here are the new patches; tested against 2.6.27-rc3.

Thanks,

Tim
1) ata.h has dst_pa in the wrong place (needs to match what the BestComm
   task microcode in bcom_ata_task.c expects); fix it.

2) The BestComm ATA task priority was changed to maximum in bestcomm_priv.h;
   this fixes a deadlock issue I was experiencing when heavy DMA was
   occuring on both the ATA and Ethernet BestComm tasks, e.g. when
   downloading a large file over a LAN to disk.

3) The ATA BestComm driver uses bcom_ata_bd which is bigger than bcom_bd
   and this causes problems because the various bcom_... functions do not
   dereference the correct location. I've introduced bcom_get_bd which
   uses bcom_task.bd_size and this fixes the problem.

Signed-off-by: Tim Yamin <[EMAIL PROTECTED]>

diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/ata.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/ata.h	2008-07-03 16:17:05.000000000 +0100
@@ -16,8 +16,8 @@
 
 struct bcom_ata_bd {
 	u32	status;
-	u32	dst_pa;
 	u32	src_pa;
+	u32	dst_pa;
 };
 
 extern struct bcom_task *
diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-07-03 16:17:05.000000000 +0100
@@ -38,7 +38,7 @@ struct bcom_task {
 	unsigned int	flags;
 	int		irq;
 
-	struct bcom_bd	*bd;
+	void		*bd;
 	phys_addr_t	bd_pa;
 	void		**cookie;
 	unsigned short	index;
@@ -140,15 +140,29 @@ bcom_queue_full(struct bcom_task *tsk)
 }
 
 /**
+ * bcom_get_bd - Get a BD from the queue
+ * @tsk: The BestComm task structure
+ * index: Index of the BD to fetch
+ */
+static inline struct bcom_bd
+*bcom_get_bd(struct bcom_task *tsk, unsigned int index)
+{
+	return tsk->bd + index * tsk->bd_size;
+}
+
+/**
  * bcom_buffer_done - Checks if a BestComm 
  * @tsk: The BestComm task structure
  */
 static inline int
 bcom_buffer_done(struct bcom_task *tsk)
 {
+	struct bcom_bd *bd;
 	if (bcom_queue_empty(tsk))
 		return 0;
-	return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+
+	bd = bcom_get_bd(tsk, tsk->outdex);
+	return !(bd->status & BCOM_BD_READY);
 }
 
 /**
@@ -160,16 +174,21 @@ bcom_buffer_done(struct bcom_task *tsk)
 static inline struct bcom_bd *
 bcom_prepare_next_buffer(struct bcom_task *tsk)
 {
-	tsk->bd[tsk->index].status = 0;	/* cleanup last status */
-	return &tsk->bd[tsk->index];
+	struct bcom_bd *bd;
+
+	bd = bcom_get_bd(tsk, tsk->index);
+	bd->status = 0;	/* cleanup last status */
+	return bd;
 }
 
 static inline void
 bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
 {
+	struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index);
+
 	tsk->cookie[tsk->index] = cookie;
 	mb();	/* ensure the bd is really up-to-date */
-	tsk->bd[tsk->index].status |= BCOM_BD_READY;
+	bd->status |= BCOM_BD_READY;
 	tsk->index = _bcom_next_index(tsk);
 	if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
 		bcom_enable(tsk);
@@ -179,10 +198,12 @@ static inline void *
 bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
 {
 	void *cookie = tsk->cookie[tsk->outdex];
+	struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex);
+
 	if (p_status)
-		*p_status = tsk->bd[tsk->outdex].status;
+		*p_status = bd->status;
 	if (p_bd)
-		*p_bd = &tsk->bd[tsk->outdex];
+		*p_bd = bd;
 	tsk->outdex = _bcom_next_outdex(tsk);
 	return cookie;
 }
diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-07-03 16:17:05.000000000 +0100
@@ -198,8 +198,8 @@ struct bcom_task_header {
 #define BCOM_IPR_SCTMR_1	2
 #define BCOM_IPR_FEC_RX		6
 #define BCOM_IPR_FEC_TX		5
-#define BCOM_IPR_ATA_RX		4
-#define BCOM_IPR_ATA_TX		3
+#define BCOM_IPR_ATA_RX		7
+#define BCOM_IPR_ATA_TX		7
 #define BCOM_IPR_SCPCI_RX	2
 #define BCOM_IPR_SCPCI_TX	2
 #define BCOM_IPR_PSC3_RX	2
This patch adds MDMA/UDMA support (using BestComm for DMA) on the MPC5200
platform. Patch requires the ATA BestComm fixes to function properly.

Based heavily on previous work by Freescale (Bernard Kuhn, John Rigby)
and Domen Puncer.

Using a SanDisk Extreme IV CF card I get read speeds of approximately
26.70 MB/sec.

Comments and testing would of course be very welcome.

Thanks,

Signed-off-by: Tim Yamin <[EMAIL PROTECTED]>

diff -urp linux-2.6.27-rc3/arch/powerpc/sysdev/bestcomm/bestcomm.c linux-2.6.27-rc3-ata/arch/powerpc/sysdev/bestcomm/bestcomm.c
--- linux-2.6.27-rc3/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.27-rc3-ata/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-07-03 16:17:05.000000000 +0100
@@ -330,11 +330,16 @@ bcom_engine_init(void)
 	/* Init 'always' initiator */
 	out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
 
+	/* If ATA DMA is enabled, always turn prefetch off (it breaks things) */
+#ifndef CONFIG_PATA_MPC52xx_DMA
 	/* Disable COMM Bus Prefetch on the original 5200; it's broken */
 	if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
+#endif
 		regval = in_be16(&bcom_eng->regs->PtdCntrl);
 		out_be16(&bcom_eng->regs->PtdCntrl,  regval | 1);
+#ifndef CONFIG_PATA_MPC52xx_DMA
 	}
+#endif
 
 	/* Init lock */
 	spin_lock_init(&bcom_eng->lock);
diff -urp linux-2.6.27-rc3/drivers/ata/Kconfig linux-2.6.27-rc3-ata/drivers/ata/Kconfig
--- linux-2.6.27-rc3/drivers/ata/Kconfig	2008-07-03 13:06:35.000000000 +0100
+++ linux-2.6.27-rc3-ata/drivers/ata/Kconfig	2008-07-03 16:16:32.000000000 +0100
@@ -462,6 +462,15 @@ config PATA_MPC52xx
 
 	  If unsure, say N.
 
+config PATA_MPC52xx_DMA
+	bool "Freescale MPC52xx SoC internal IDE DMA (Experimental)"
+	depends on PATA_MPC52xx
+	help
+	  This option enables support for DMA on the MPC52xx SoC PATA
+	  controller.
+
+	  If unsure, say N.
+
 config PATA_MPIIX
 	tristate "Intel PATA MPIIX support"
 	depends on PCI
diff -urp linux-2.6.27-rc3/drivers/ata/pata_mpc52xx.c linux-2.6.27-rc3-ata/drivers/ata/pata_mpc52xx.c
--- linux-2.6.27-rc3/drivers/ata/pata_mpc52xx.c	2008-07-03 13:06:35.000000000 +0100
+++ linux-2.6.27-rc3-ata/drivers/ata/pata_mpc52xx.c	2008-07-03 16:16:32.000000000 +0100
@@ -6,6 +6,9 @@
  * Copyright (C) 2006 Sylvain Munaut <[EMAIL PROTECTED]>
  * Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt
  *
+ * UDMA support based on patches by Freescale (Bernard Kuhn, John Rigby),
+ * Domen Puncer and Tim Yamin.
+ *
  * This file is licensed under the terms of the GNU General Public License
  * version 2. This program is licensed "as is" without any warranty of any
  * kind, whether express or implied.
@@ -17,28 +20,47 @@
 #include <linux/delay.h>
 #include <linux/libata.h>
 #include <linux/of_platform.h>
 
+#include <asm/cacheflush.h>
 #include <asm/types.h>
 #include <asm/prom.h>
 #include <asm/mpc52xx.h>
 
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <sysdev/bestcomm/ata.h>
 
 #define DRV_NAME	"mpc52xx_ata"
 #define DRV_VERSION	"0.1.2"
 
-
 /* Private structures used by the driver */
 struct mpc52xx_ata_timings {
 	u32	pio1;
 	u32	pio2;
+	u32	mdma1;
+	u32	mdma2;
+	u32	udma1;
+	u32	udma2;
+	u32	udma3;
+	u32	udma4;
+	u32	udma5;
+	int	using_udma;
 };
 
 struct mpc52xx_ata_priv {
 	unsigned int			ipb_period;
 	struct mpc52xx_ata __iomem *	ata_regs;
+	phys_addr_t			ata_regs_pa;
 	int				ata_irq;
 	struct mpc52xx_ata_timings	timings[2];
 	int				csel;
+
+	/* DMA */
+	struct bcom_task		*dmatsk;
+	const struct udmaspec		*udmaspec;
+	const struct mdmaspec		*mdmaspec;
+	int 				mpc52xx_ata_dma_last_write;
+	int				waiting_for_dma;
 };
 
 
@@ -53,6 +75,107 @@ static const int ataspec_ta[5]    = { 35
 
 #define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
 
+/* ======================================================================== */
+
+/* ATAPI-4 MDMA specs (in clocks) */
+struct mdmaspec {
+	u32 t0M;
+	u32 td;
+	u32 th;
+	u32 tj;
+	u32 tkw;
+	u32 tm;
+	u32 tn;
+};
+
+static const struct mdmaspec mdmaspec66[3] = {
+	{ .t0M = 32, .td = 15, .th = 2, .tj = 2, .tkw = 15, .tm = 4, .tn = 1 },
+	{ .t0M = 10, .td = 6,  .th = 1, .tj = 1, .tkw = 4,  .tm = 2, .tn = 1 },
+	{ .t0M = 8,  .td = 5,  .th = 1, .tj = 1, .tkw = 2,  .tm = 2, .tn = 1 },
+};
+
+static const struct mdmaspec mdmaspec132[3] = {
+	{ .t0M = 64, .td = 29, .th = 3, .tj = 3, .tkw = 29, .tm = 7, .tn = 2 },
+	{ .t0M = 20, .td = 11, .th = 2, .tj = 1, .tkw = 7,  .tm = 4, .tn = 1 },
+	{ .t0M = 16, .td = 10, .th = 2, .tj = 1, .tkw = 4,  .tm = 4, .tn = 1 },
+};
+
+/* ATAPI-4 UDMA specs (in clocks) */
+struct udmaspec {
+	u32 tcyc;
+	u32 t2cyc;
+	u32 tds;
+	u32 tdh;
+	u32 tdvs;
+	u32 tdvh;
+	u32 tfs;
+	u32 tli;
+	u32 tmli;
+	u32 taz;
+	u32 tzah;
+	u32 tenv;
+	u32 tsr;
+	u32 trfs;
+	u32 trp;
+	u32 tack;
+	u32 tss;
+};
+
+static const struct udmaspec udmaspec66[6] = {
+	{ .tcyc = 8,  .t2cyc = 16, .tds  = 1,  .tdh  = 1, .tdvs = 5,  .tdvh = 1,
+	  .tfs  = 16, .tli   = 10, .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 3,  .trfs  = 5,  .trp  = 11, .tack = 2, .tss  = 4,
+	},
+	{ .tcyc = 5,  .t2cyc = 11, .tds  = 1,  .tdh  = 1, .tdvs = 4,  .tdvh = 1,
+	  .tfs  = 14, .tli   = 10, .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 5,  .trp  = 9,  .tack = 2, .tss  = 4,
+	},
+	{ .tcyc = 4,  .t2cyc = 8,  .tds  = 1,  .tdh  = 1, .tdvs = 3,  .tdvh = 1,
+	  .tfs  = 12, .tli   = 10, .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 4,  .trp  = 7,  .tack = 2, .tss  = 4,
+	},
+	{ .tcyc = 3,  .t2cyc = 6,  .tds  = 1,  .tdh  = 1, .tdvs = 2,  .tdvh = 1,
+	  .tfs  = 9,  .tli   = 7,  .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 4,  .trp  = 7,  .tack = 2, .tss  = 4,
+	},
+	{ .tcyc = 2,  .t2cyc = 4,  .tds  = 1,  .tdh  = 1, .tdvs = 1,  .tdvh = 1,
+	  .tfs  = 8,  .tli   = 8,  .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 4,  .trp  = 7,  .tack = 2, .tss  = 4,
+	},
+	{ .tcyc = 2,  .t2cyc = 2,  .tds  = 1,  .tdh  = 1, .tdvs = 1,  .tdvh = 1,
+	  .tfs  = 6,  .tli   = 5,  .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 4,  .trp  = 6,  .tack = 2, .tss  = 4,
+	},
+};
+
+static const struct udmaspec udmaspec132[6] = {
+	{ .tcyc = 15, .t2cyc = 31, .tds  = 2,  .tdh  = 1, .tdvs = 10, .tdvh = 1,
+	  .tfs  = 30, .tli   = 20, .tmli = 3,  .taz  = 2, .tzah = 3,  .tenv = 3,
+	  .tsr  = 7,  .trfs  = 10, .trp  = 22, .tack = 3, .tss  = 7,
+	},
+	{ .tcyc = 10, .t2cyc = 21, .tds  = 2,  .tdh  = 1, .tdvs = 7,  .tdvh = 1,
+	  .tfs  = 27, .tli   = 20, .tmli = 3,  .taz  = 2, .tzah = 3,  .tenv = 3,
+	  .tsr  = 4,  .trfs  = 10, .trp  = 17, .tack = 3, .tss  = 7,
+	},
+	{ .tcyc = 6,  .t2cyc = 12, .tds  = 1,  .tdh  = 1, .tdvs = 5,  .tdvh = 1,
+	  .tfs  = 23, .tli   = 20, .tmli = 3,  .taz  = 2, .tzah = 3,  .tenv = 3,
+	  .tsr  = 3,  .trfs  = 8,  .trp  = 14, .tack = 3, .tss  = 7,
+	},
+	{ .tcyc = 7,  .t2cyc = 12, .tds  = 1,  .tdh  = 1, .tdvs = 3,  .tdvh = 1,
+	  .tfs  = 15, .tli   = 13, .tmli = 3,  .taz  = 2, .tzah = 3,  .tenv = 3,
+	  .tsr  = 3,  .trfs  = 8,  .trp  = 14, .tack = 3, .tss  = 7,
+	},
+	{ .tcyc = 2,  .t2cyc = 5,  .tds  = 0,  .tdh  = 0, .tdvs = 1,  .tdvh = 1,
+	  .tfs  = 16, .tli   = 14, .tmli = 2,  .taz  = 1, .tzah = 2,  .tenv = 2,
+	  .tsr  = 2,  .trfs  = 7,  .trp  = 13, .tack = 2, .tss  = 6,
+	},
+	{ .tcyc = 3,  .t2cyc = 6,  .tds  = 1,  .tdh  = 1, .tdvs = 1,  .tdvh = 1,
+	  .tfs  = 12, .tli   = 10, .tmli = 3,  .taz  = 2, .tzah = 3,  .tenv = 3,
+	  .tsr  = 3,  .trfs  = 7,  .trp  = 12, .tack = 3, .tss  = 7,
+	},
+};
+
+/* ======================================================================== */
 
 /* Bit definitions inside the registers */
 #define MPC52xx_ATA_HOSTCONF_SMR	0x80000000UL /* State machine reset */
@@ -66,6 +189,7 @@ static const int ataspec_ta[5]    = { 35
 #define MPC52xx_ATA_HOSTSTAT_WERR	0x01000000UL /* Write Error */
 
 #define MPC52xx_ATA_FIFOSTAT_EMPTY	0x01 /* FIFO Empty */
+#define MPC52xx_ATA_FIFOSTAT_ERROR	0x40 /* FIFO Error */
 
 #define MPC52xx_ATA_DMAMODE_WRITE	0x01 /* Write DMA */
 #define MPC52xx_ATA_DMAMODE_READ	0x02 /* Read DMA */
@@ -75,6 +199,8 @@ static const int ataspec_ta[5]    = { 35
 #define MPC52xx_ATA_DMAMODE_FR		0x20 /* FIFO Reset */
 #define MPC52xx_ATA_DMAMODE_HUT		0x40 /* Host UDMA burst terminate */
 
+#define MAX_DMA_BUFFERS 128
+#define MAX_DMA_BUFFER_SIZE 0x20000u
 
 /* Structure of the hardware registers */
 struct mpc52xx_ata {
@@ -165,6 +294,41 @@ mpc52xx_ata_compute_pio_timings(struct m
 	return 0;
 }
 
+static int
+mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
+{
+	struct mpc52xx_ata_timings *timing = &priv->timings[dev];
+	const struct mdmaspec *s = &priv->mdmaspec[speed];
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	timing->mdma1 = (s->t0M << 24) | (s->td << 16) | (s->tkw << 8) | (s->tm);
+	timing->mdma2 = (s->th << 24) | (s->tj << 16) | (s->tn << 8);
+	timing->using_udma = 0;
+
+	return 0;
+}
+
+static int
+mpc52xx_ata_compute_udma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
+{
+	struct mpc52xx_ata_timings *timing = &priv->timings[dev];
+	const struct udmaspec *s = &priv->udmaspec[speed];
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	timing->udma1 = (s->t2cyc << 24) | (s->tcyc << 16) | (s->tds << 8) | (s->tdh);
+	timing->udma2 = (s->tdvs << 24) | (s->tdvh << 16) | (s->tfs << 8) | (s->tli);
+	timing->udma3 = (s->tmli << 24) | (s->taz << 16) | (s->tenv << 8) | (s->tsr);
+	timing->udma4 = (s->tss << 24) | (s->trfs << 16) | (s->trp << 8) | (s->tack);
+	timing->udma5 = (s->tzah << 24);
+	timing->using_udma = 1;
+
+	return 0;
+}
+
 static void
 mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
 {
@@ -173,14 +337,13 @@ mpc52xx_ata_apply_timings(struct mpc52xx
 
 	out_be32(&regs->pio1,  timing->pio1);
 	out_be32(&regs->pio2,  timing->pio2);
-	out_be32(&regs->mdma1, 0);
-	out_be32(&regs->mdma2, 0);
-	out_be32(&regs->udma1, 0);
-	out_be32(&regs->udma2, 0);
-	out_be32(&regs->udma3, 0);
-	out_be32(&regs->udma4, 0);
-	out_be32(&regs->udma5, 0);
-
+	out_be32(&regs->mdma1, timing->mdma1);
+	out_be32(&regs->mdma2, timing->mdma2);
+	out_be32(&regs->udma1, timing->udma1);
+	out_be32(&regs->udma2, timing->udma2);
+	out_be32(&regs->udma3, timing->udma3);
+	out_be32(&regs->udma4, timing->udma4);
+	out_be32(&regs->udma5, timing->udma5);
 	priv->csel = device;
 }
 
@@ -245,6 +408,29 @@ mpc52xx_ata_set_piomode(struct ata_port 
 	mpc52xx_ata_apply_timings(priv, adev->devno);
 }
 static void
+mpc52xx_ata_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	int rv;
+
+	if (adev->dma_mode >= XFER_UDMA_0) {
+		int dma = adev->dma_mode - XFER_UDMA_0;
+		rv = mpc52xx_ata_compute_udma_timings(priv, adev->devno, dma);
+	} else {
+		int dma = adev->dma_mode - XFER_MW_DMA_0;
+		rv = mpc52xx_ata_compute_mdma_timings(priv, adev->devno, dma);
+	}
+
+	if (rv) {
+		dev_alert(ap->dev,
+			"Trying to select invalid DMA mode %d\n",
+			adev->dma_mode);
+		return;
+	}
+
+	mpc52xx_ata_apply_timings(priv, adev->devno);
+}
+static void
 mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device)
 {
 	struct mpc52xx_ata_priv *priv = ap->host->private_data;
@@ -255,16 +441,187 @@ mpc52xx_ata_dev_select(struct ata_port *
 	ata_sff_dev_select(ap,device);
 }
 
+static int
+mpc52xx_ata_build_dmatable(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE), si;
+	struct scatterlist *sg;
+	int count = 0;
+
+	if (read)
+		bcom_ata_rx_prepare(priv->dmatsk);
+	else
+		bcom_ata_tx_prepare(priv->dmatsk);
+
+	for_each_sg(qc->sg, sg, qc->n_elem, si) {
+		dma_addr_t cur_addr = sg_dma_address(sg);
+		u32 cur_len = sg_dma_len(sg);
+
+		while (cur_len) {
+			unsigned int tc = min(cur_len, MAX_DMA_BUFFER_SIZE);
+			struct bcom_ata_bd *bd = (struct bcom_ata_bd *) bcom_prepare_next_buffer(priv->dmatsk);
+
+			if (read) {
+				bd->status = tc;
+				bd->src_pa = (__force u32) priv->ata_regs_pa +
+					offsetof(struct mpc52xx_ata, fifo_data);
+				bd->dst_pa = (__force u32) cur_addr;
+			} else {
+				bd->status = tc;
+				bd->src_pa = (__force u32) cur_addr;
+				bd->dst_pa = (__force u32) priv->ata_regs_pa +
+					offsetof(struct mpc52xx_ata, fifo_data);
+			}
+
+			bcom_submit_next_buffer(priv->dmatsk, NULL);
+
+			cur_addr += tc;
+			cur_len -= tc;
+			count++;
+
+			if (count > MAX_DMA_BUFFERS) {
+				dev_alert(ap->dev, "dma table"
+					"too small\n");
+				goto use_pio_instead;
+			}
+		}
+	}
+	return 1;
+
+ use_pio_instead:
+	bcom_ata_reset_bd(priv->dmatsk);
+	return 0;
+}
+
+static void
+mpc52xx_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	struct mpc52xx_ata __iomem *regs = priv->ata_regs;
+
+	unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE);
+	u8 dma_mode;
+
+	if (!mpc52xx_ata_build_dmatable(qc))
+		dev_alert(ap->dev, "%s: %i, return 1?\n",
+			__func__, __LINE__);
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+		dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+
+	if (read) {
+		dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_READ |
+				MPC52xx_ATA_DMAMODE_FE;
+
+		/* Setup FIFO if direction changed */
+		if (priv->mpc52xx_ata_dma_last_write != 0) {
+			priv->mpc52xx_ata_dma_last_write = 0;
+
+			/* Configure FIFO with granularity to 7 */
+			out_8(&regs->fifo_control, 7);
+			out_be16(&regs->fifo_alarm, 128);
+
+			/* Set FIFO Reset bit (FR) */
+			out_8(&regs->dma_mode, MPC52xx_ATA_DMAMODE_FR);
+		}
+	} else {
+		dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_WRITE;
+
+		/* Setup FIFO if direction changed */
+		if (priv->mpc52xx_ata_dma_last_write != 1) {
+			priv->mpc52xx_ata_dma_last_write = 1;
+
+			/* Configure FIFO with granularity to 4 */
+			out_8(&regs->fifo_control, 4);
+			out_be16(&regs->fifo_alarm, 128);
+		}
+	}
+
+	if (priv->timings[qc->dev->devno].using_udma)
+		dma_mode |= MPC52xx_ATA_DMAMODE_UDMA;
+
+	out_8(&regs->dma_mode, dma_mode);
+	priv->waiting_for_dma = ATA_DMA_ACTIVE;
+
+	ata_wait_idle(ap);
+	ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void
+mpc52xx_bmdma_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	bcom_set_task_auto_start(priv->dmatsk->tasknum, priv->dmatsk->tasknum);
+	bcom_enable(priv->dmatsk);
+}
+
+static void
+mpc52xx_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	bcom_disable(priv->dmatsk);
+	bcom_ata_reset_bd(priv->dmatsk);
+	priv->waiting_for_dma = 0;
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+		dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+}
+
+static u8
+mpc52xx_bmdma_status(struct ata_port *ap)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR) {
+		dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+		return priv->waiting_for_dma | ATA_DMA_ERR;
+	}
+
+	return priv->waiting_for_dma;
+}
+
+static irqreturn_t
+mpc52xx_ata_task_irq(int irq, void *vpriv)
+{
+	struct mpc52xx_ata_priv *priv = vpriv;
+	while (bcom_buffer_done(priv->dmatsk))
+		bcom_retrieve_buffer(priv->dmatsk, NULL, NULL);
+	
+	priv->waiting_for_dma |= ATA_DMA_INTR;
+
+	return IRQ_HANDLED;
+}
+
 static struct scsi_host_template mpc52xx_ata_sht = {
 	ATA_PIO_SHT(DRV_NAME),
 };
 
 static struct ata_port_operations mpc52xx_ata_port_ops = {
 	.inherits		= &ata_sff_port_ops,
-	.sff_dev_select		= mpc52xx_ata_dev_select,
-	.cable_detect		= ata_cable_40wire,
+
 	.set_piomode		= mpc52xx_ata_set_piomode,
-	.post_internal_cmd	= ATA_OP_NULL,
+	.set_dmamode		= mpc52xx_ata_set_dmamode,
+	.sff_dev_select		= mpc52xx_ata_dev_select,
+
+	.bmdma_setup		= mpc52xx_bmdma_setup,
+	.bmdma_start		= mpc52xx_bmdma_start,
+	.bmdma_stop		= mpc52xx_bmdma_stop,
+	.bmdma_status		= mpc52xx_bmdma_status,
+
+	.qc_prep		= ata_noop_qc_prep,
 };
 
 static int __devinit
@@ -281,9 +643,11 @@ mpc52xx_ata_init_one(struct device *dev,
 
 	ap = host->ports[0];
 	ap->flags		|= ATA_FLAG_SLAVE_POSS;
-	ap->pio_mask		= 0x1f;	/* Up to PIO4 */
-	ap->mwdma_mask		= 0x00;	/* No MWDMA   */
-	ap->udma_mask		= 0x00;	/* No UDMA    */
+	ap->pio_mask		= ATA_PIO4;	/* Up to PIO4 */
+#ifdef CONFIG_PATA_MPC52xx_DMA
+	ap->mwdma_mask		= ATA_MWDMA2;	/* Up to MWDMA2 */
+	ap->udma_mask		= ATA_UDMA2;	/* Up to UDMA2 */
+#endif
 	ap->ops			= &mpc52xx_ata_port_ops;
 	host->private_data	= priv;
 
@@ -333,7 +697,7 @@ mpc52xx_ata_probe(struct of_device *op, 
 	int ata_irq;
 	struct mpc52xx_ata __iomem *ata_regs;
 	struct mpc52xx_ata_priv *priv;
-	int rv;
+	int rv, ret, task_irq;
 
 	/* Get ipb frequency */
 	ipb_freq = mpc52xx_find_ipb_freq(op->node);
@@ -389,8 +753,32 @@ mpc52xx_ata_probe(struct of_device *op, 
 
 	priv->ipb_period = 1000000000 / (ipb_freq / 1000);
 	priv->ata_regs = ata_regs;
+	priv->ata_regs_pa = res_mem.start;
 	priv->ata_irq = ata_irq;
 	priv->csel = -1;
+	priv->mpc52xx_ata_dma_last_write = -1;
+
+	if (ipb_freq/1000000 == 66) {
+		priv->mdmaspec = mdmaspec66;
+		priv->udmaspec = udmaspec66;
+	} else {
+		priv->mdmaspec = mdmaspec132;
+		priv->udmaspec = udmaspec132;
+	}
+
+	priv->dmatsk = bcom_ata_init(MAX_DMA_BUFFERS, MAX_DMA_BUFFER_SIZE);
+	if (!priv->dmatsk) {
+		dev_alert(&op->dev, "Failed to initialize BestComm task!\n");
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	task_irq = bcom_get_task_irq(priv->dmatsk);
+	ret = request_irq(task_irq, &mpc52xx_ata_task_irq, IRQF_DISABLED,
+				"ATA task", priv);
+	if (ret)
+		dev_alert(&op->dev, "request_irq failed with: "
+					"%i\n", ret);
 
 	/* Init the hw */
 	rv = mpc52xx_ata_hw_init(priv);
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to