linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH]: [MPC5200] Add ATA DMA support
@ 2008-09-05 18:29 WITTROCK
  0 siblings, 0 replies; 24+ messages in thread
From: WITTROCK @ 2008-09-05 18:29 UTC (permalink / raw)
  To: linuxppc-dev



Tim Yamin-2 wrote:
> 
> On Wed, Aug 13, 2008 at 7:11 AM, Grant Likely <grant.likely@secretlab.ca>
> 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
> 



I have been trying this patch against 2.6.26.3 on our own MPC5200B based
board.  I am using a seagate  ST980815A.  The drive mounts O.K, and I can
transfer small files, but during large transfers there is the following
problem.

.....

## Booting kernel from Legacy Image at 00500000 ...
   Image Name:   Linux-2.6.26.3
   Created:      2008-09-05  17:01:31 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1409558 Bytes =  1.3 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
## Flattened Device Tree blob at 00400000
   Booting using the fdt blob at 0x400000
[    0.000000] Using m9000 machine description
[    0.000000] Linux version 2.6.26.3 (developer@usi-dev-linux) (gcc version
4.1.2) #2 Fri Sep 5 13:01:26 EDT 2008
[    0.000000] Zone PFN ranges:
[    0.000000]   DMA             0 ->    32767
[    0.000000]   Normal      32767 ->    32767

........

[    0.796907] Driver 'sd' needs updating - please use bus_type methods
[    0.803944] ata: MPC52xx IDE/ATA libata driver
[    0.809300] scsi0 : mpc52xx_ata
[    0.813246] ata1: PATA max UDMA/33 ata_regs 0xf0003a00 irq 135
[    0.980196] ata1.00: ATA-6: ST980815A, 3.ALC, max UDMA/100
[    0.985864] ata1.00: 156301488 sectors, multi 0: LBA48 
[    1.004074] ata1.00: configured for UDMA/33
[    1.009047] scsi 0:0:0:0: Direct-Access     ATA      ST980815A       
3.AL PQ: 0 ANSI: 5
[    1.018652] sd 0:0:0:0: [sda] 156301488 512-byte hardware sectors (80026
MB)
[    1.026104] sd 0:0:0:0: [sda] Write Protect is off
[    1.031419] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled,
doesn't support DPO or FUA
[    1.041235] sd 0:0:0:0: [sda] 156301488 512-byte hardware sectors (80026
MB)
[    1.048681] sd 0:0:0:0: [sda] Write Protect is off
[    1.053994] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled,
doesn't support DPO or FUA
[    1.063308]  sda: unknown partition table
[    1.077092] sd 0:0:0:0: [sda] Attached SCSI disk

.......

-sh-2.05b# 
-sh-2.05b# cp test.txt > /mnt/hd/writetest
-sh-2.05b# cp test2.txt > /mnt/hd/writetest
-sh-2.05b# cp -R  /test /mnt/hd/writetest
[  170.239726] ata1.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6
frozen
[  170.247020] ata1.00: cmd ca/00:18:30:00:00/00:00:00:00:00/e0 tag 0 dma
12288 out
[  170.247033]          res 40/00:00:00:00:00/00:00:00:00:00/00 Emask 0x4
(timeout)
[  170.262180] ata1.00: status: { DRDY }
[  170.265995] ata1: soft resetting link
[  170.423867] ata1.00: revalidation failed (errno=-2)
[  170.428902] ata1: failed to recover some devices, retrying in 5 secs
[  175.435694] ata1: soft resetting link
[  175.591873] ata1.00: revalidation failed (errno=-2)
[  175.596909] ata1: failed to recover some devices, retrying in 5 secs
[  180.603697] ata1: soft resetting link
[  180.759873] ata1.00: revalidation failed (errno=-2)
[  180.764907] ata1.00: disabled
[  181.271725] ata1: soft resetting link
[  181.427741] ata1: EH complete
[  181.430856] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.437358] end_request: I/O error, dev sda, sector 48
[  181.442652] Buffer I/O error on device sda, logical block 6
[  181.448386] lost page write due to I/O error on sda
[  181.453429] Buffer I/O error on device sda, logical block 7
[  181.459165] lost page write due to I/O error on sda
[  181.464205] Buffer I/O error on device sda, logical block 8
[  181.469939] lost page write due to I/O error on sda
[  181.475076] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.481580] end_request: I/O error, dev sda, sector 8
[  181.486782] Buffer I/O error on device sda, logical block 1
[  181.492515] lost page write due to I/O error on sda
[  181.497653] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.504158] end_request: I/O error, dev sda, sector 0
[  181.509361] Buffer I/O error on device sda, logical block 0
[  181.515091] lost page write due to I/O error on sda
[  181.520252] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.526762] end_request: I/O error, dev sda, sector 180232
[  181.532441] Buffer I/O error on device sda, logical block 22540
[  181.538534] lost page write due to I/O error on sda
[  181.543784] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.550259] end_request: I/O error, dev sda, sector 180328
[  181.556223] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.562707] end_request: I/O error, dev sda, sector 181352
[  181.568659] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.575144] end_request: I/O error, dev sda, sector 182376
[  181.581094] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.587579] end_request: I/O error, dev sda, sector 183400
[  181.593483] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  181.599997] end_request: I/O error, dev sda, sector 184424
[  185.268091] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  185.274573] end_request: I/O error, dev sda, sector 180224
[  185.280329] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  185.286803] end_request: I/O error, dev sda, sector 64
[  185.292100] Buffer I/O error on device sda, logical block 8
[  185.297830] lost page write due to I/O error on sda
[  185.302957] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  185.309462] end_request: I/O error, dev sda, sector 48
[  185.314756] Buffer I/O error on device sda, logical block 6
[  185.320493] lost page write due to I/O error on sda
[  185.325593] sd 0:0:0:0: [sda] Result: hostbyte=0x04 driverbyte=0x00
[  185.332095] end_request: I/O error, dev sda, sector 0
[  185.337299] Buffer I/O error on device sda, logical block 0
[  185.343028] lost page write due to I/O error on sda
[  185.348067] Buffer I/O error on device sda, logical block 1
[  185.353805] lost page write due to I/O error on sda

With PATA_MPC52xx_DMA not set, it works fine.  To help eliminate our
hardware, I also tried this on a lite5200b development board, and had the
same results.  I also tried this with a compact flash card, and then the
problem occurs, but it happens during bootup.  I can't be certain my cf card
works with DMA though, as I don't have anything else handy to test it with.

I do have a driver which communicates with an FPGA using the LocalPlus bus,
but for the purpose of testing I don't have it installed, and the FPGA I/O
is tristated.

I recall reading somewhere that the lite5200B board had some problem with
IDE signal integrity when using a regular IDE cable, so I used an ultra-IDE
cable with the same results.  On our own hardware, there is no cable as the
drive mounts directly to a connector on our board within ~3" of the
processor.

Any thoughts.....

Thanks,
-Jeff
-- 
View this message in context: http://www.nabble.com/-PATCH-%3A--MPC5200--Add-ATA-DMA-support-tp17880454p19336474.html
Sent from the linuxppc-dev mailing list archive at Nabble.com.

^ permalink raw reply	[flat|nested] 24+ messages in thread
[parent not found: <DD39B5C3F4963040ADC9768BE7E430CB03202F4F@is-hdq-exchange.marel.net>]
* [PATCH]: [MPC5200] Add ATA DMA support
@ 2008-06-17  8:28 Tim Yamin
  2008-08-06 11:58 ` Daniel Schnell
  0 siblings, 1 reply; 24+ messages in thread
From: Tim Yamin @ 2008-06-17  8:28 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 1612 bytes --]

This patch adds MDMA/UDMA support (using BestComm for DMA) on the MPC5200
platform.

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.

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.

There's also what I believe to be a hardware bug if you have high levels
of BestComm ATA DMA activity along with heavy LocalPlus Bus activity;
the address bus seems to sometimes get corrupted with ATA commands while
the LocalPlus Bus operation is still active (i.e. Chip Select is asserted).

I've asked Freescale about this but have not received a reply yet -- if
anybody from Freescale has any ideas please contact me; I can supply some
analyzer traces if needed. Therefore, for now, do not enable DMA if you
need reliable LocalPlus Bus unless you do a fixup in your driver as
follows:

Locking example:

        while (test_and_set_bit(0, &pata_mpc52xx_ata_dma_lock) != 0)
        {
                struct bcom_task_2 *tsk = pata_mpc52xx_ata_dma_task;

                if(bcom_buffer_done_2(tsk))
                        return 1;
        }

	return 0;

(Save the return value to `flags`)

Unlocking example:

        if(flags == 0)
                clear_bit(0, &pata_mpc52xx_ata_dma_lock);

Comments and testing would of course be very welcome.

Thanks,

Signed-off-by: Tim Yamin <plasm@roo.me.uk>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 1050-mpc5200-add-ATA-DMA.patch --]
[-- Type: text/x-patch; name=1050-mpc5200-add-ATA-DMA.patch, Size: 23417 bytes --]

This patch adds MDMA/UDMA support (using BestComm for DMA) on the MPC5200
platform.

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.

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.

There's also what I believe to be a hardware bug if you have high levels
of BestComm ATA DMA activity along with heavy LocalPlus Bus activity;
the address bus seems to sometimes get corrupted with ATA commands while
the LocalPlus Bus operation is still active (i.e. Chip Select is asserted).

I've asked Freescale about this but have not received a reply yet -- if
anybody from Freescale has any ideas please contact me; I can supply some
analyzer traces if needed. Therefore, for now, do not enable DMA if you
need reliable LocalPlus Bus unless you do a fixup in your driver as
follows:

Locking example:

        while (test_and_set_bit(0, &pata_mpc52xx_ata_dma_lock) != 0)
        {
                struct bcom_task_2 *tsk = pata_mpc52xx_ata_dma_task;

                if(bcom_buffer_done_2(tsk))
                        return 1;
        }

	return 0;

(Save the return value to `flags`)

Unlocking example:

        if(flags == 0)
                clear_bit(0, &pata_mpc52xx_ata_dma_lock);

Comments and testing would of course be very welcome.

Thanks,

Signed-off-by: Tim Yamin <plasm@roo.me.uk>

diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/ata.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h	2008-03-18 15:49:53.000000000 +0000
+++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/ata.h	2008-04-15 10:42:38.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 -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.c
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-03-18 15:49:53.000000000 +0000
+++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-04-15 10:42:38.000000000 +0100
@@ -330,11 +330,10 @@
 	/* Init 'always' initiator */
 	out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
 
-	/* Disable COMM Bus Prefetch on the original 5200; it's broken */
-	if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
-		regval = in_be16(&bcom_eng->regs->PtdCntrl);
-		out_be16(&bcom_eng->regs->PtdCntrl,  regval | 1);
-	}
+	/* Disable COMM Bus Prefetch; ATA DMA does not work properly with it
+	   enabled. */
+	regval = in_be16(&bcom_eng->regs->PtdCntrl);
+	out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
 
 	/* Init lock */
 	spin_lock_init(&bcom_eng->lock);
diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-03-18 15:49:53.000000000 +0000
+++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-04-15 10:42:38.000000000 +0100
@@ -17,6 +17,7 @@
 #define __BESTCOMM_H__
 
 struct bcom_bd; /* defined later on ... */
+struct bcom_bd_2;
 
 
 /* ======================================================================== */
@@ -49,6 +50,22 @@
 	void*		priv;
 };
 
+struct bcom_task_2 {
+	unsigned int	tasknum;
+	unsigned int	flags;
+	int		irq;
+
+	struct bcom_bd_2	*bd;
+	phys_addr_t	bd_pa;
+	void		**cookie;
+	unsigned short	index;
+	unsigned short	outdex;
+	unsigned int	num_bd;
+	unsigned int	bd_size;
+
+	void*		priv;
+};
+
 #define BCOM_FLAGS_NONE         0x00000000ul
 #define BCOM_FLAGS_ENABLE_TASK  (1ul <<  0)
 
@@ -95,6 +112,11 @@
 	u32	data[1];	/* variable, but at least 1 */
 };
 
+struct bcom_bd_2 {
+	u32	status;
+	u32	data[2];	/* variable, but at least 2 */
+};
+
 #define BCOM_BD_READY	0x40000000ul
 
 /** _bcom_next_index - Get next input index.
@@ -108,6 +130,12 @@
 	return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1;
 }
 
+static inline int
+_bcom_next_index_2(struct bcom_task_2 *tsk)
+{
+	return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1;
+}
+
 /** _bcom_next_outdex - Get next output index.
  * @tsk: pointer to task structure
  *
@@ -129,6 +157,12 @@
 	return tsk->index == tsk->outdex;
 }
 
+static inline int
+bcom_queue_empty_2(struct bcom_task_2 *tsk)
+{
+	return tsk->index == tsk->outdex;
+}
+
 /**
  * bcom_queue_full - Checks if a BestComm task BD queue is full
  * @tsk: The BestComm task structure
@@ -151,6 +185,14 @@
 	return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
 }
 
+static inline int
+bcom_buffer_done_2(struct bcom_task_2 *tsk)
+{
+	if (bcom_queue_empty_2(tsk))
+		return 0;
+	return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+}
+
 /**
  * bcom_prepare_next_buffer - clear status of next available buffer.
  * @tsk: The BestComm task structure
@@ -175,6 +217,22 @@
 		bcom_enable(tsk);
 }
 
+static inline struct bcom_bd_2 *
+bcom_prepare_next_buffer_2(struct bcom_task_2 *tsk)
+{
+	tsk->bd[tsk->index].status = 0;	/* cleanup last status */
+	return &tsk->bd[tsk->index];
+}
+
+static inline void
+bcom_submit_next_buffer_2(struct bcom_task_2 *tsk, void *cookie)
+{
+	tsk->cookie[tsk->index] = cookie;
+	mb();	/* ensure the bd is really up-to-date */
+	tsk->bd[tsk->index].status |= BCOM_BD_READY;
+	tsk->index = _bcom_next_index_2(tsk);
+}
+
 static inline void *
 bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
 {
diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-03-18 15:49:53.000000000 +0000
+++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-04-15 10:42:38.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
diff -Nurp linux-2.6.26-rc6/drivers/ata/Kconfig linux-2.6.26-rc6.new/drivers/ata/Kconfig
--- linux-2.6.26-rc6/drivers/ata/Kconfig	2008-03-18 15:49:33.000000000 +0000
+++ linux-2.6.26-rc6.new/drivers/ata/Kconfig	2008-04-15 10:41:51.000000000 +0100
@@ -462,6 +462,15 @@
 
 	  If unsure, say N.
 
+config PATA_MPC52xx_DMA
+	tristate "Freescale MPC52xx SoC internal IDE DMA"
+	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 -Nurp linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c linux-2.6.26-rc6.new/drivers/ata/pata_mpc52xx.c
--- linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c	2008-03-18 15:49:33.000000000 +0000
+++ linux-2.6.26-rc6.new/drivers/ata/pata_mpc52xx.c	2008-04-15 10:41:49.000000000 +0100
@@ -6,6 +6,9 @@
  * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
  * 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 <asm/cacheflush.h>
 #include <asm/types.h>
 #include <asm/prom.h>
 #include <asm/of_platform.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;
+	struct mpc52xx_ata __iomem *	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,102 @@
 
 #define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
 
+/* ATAPI-4 MDMA specs (in clocks) */
+struct mdmaspec {
+	u32 t0M[3];
+	u32 td[3];
+	u32 th[3];
+	u32 tj[3];
+	u32 tkw[3];
+	u32 tm[3];
+	u32 tn[3];
+};
+
+// -----------------------------------------------------------------------------------------------
+
+static const struct mdmaspec mdmaspec66 = {
+	{32,  10,  8},
+	{15,  6,   5},
+	{2,   1,   1},
+	{2,   1,   1},
+	{15,  4,   2},
+	{4,   2,   2},
+	{1,   1,   1}
+};
+
+static const struct mdmaspec mdmaspec132 = {
+	{64,  20,  16},
+	{29,  11,  10},
+	{3,   2,   2},
+	{3,   1,   1},
+	{29,  7,   4},
+	{7,   4,   4},
+	{2,   1,   1}
+};
+
+
+/* ATAPI-4 UDMA specs (in clocks) */
+struct udmaspec {
+	u32 tcyc[6];
+	u32 t2cyc[6];
+	u32 tds[6];
+	u32 tdh[6];
+	u32 tdvs[6];
+	u32 tdvh[6];
+	u32 tfs_min[6];
+	u32 tli_max[6];
+	u32 tmli[6];
+	u32 taz[6];
+	u32 tzah[6];
+	u32 tenv_min[6];
+	u32 tsr[6];
+	u32 trfs[6];
+	u32 trp[6];
+	u32 tack[6];
+	u32 tss[6];
+};
+
+static const struct udmaspec udmaspec66 = {
+	{ 8,  5,  4,  3,  2,  2},
+	{16, 11,  8,  6,  4 , 2},
+	{ 1,  1,  1,  1,  1,  1},
+	{ 1,  1,  1,  1,  1,  1},
+	{ 5,  4,  3,  2,  1,  1},
+	{ 1,  1,  1,  1,  1,  1},
+	{16, 14, 12,  9,  8,  6},
+	{10, 10, 10,  7,  8,  5},
+	{ 2,  2,  2,  2,  2,  2},
+	{ 1,  1,  1,  1,  1,  1},
+	{ 2,  2,  2,  2,  2,  2},
+	{ 2,  2,  2,  2,  2,  2},
+	{ 3,  2,  2,  2,  2,  2},
+	{ 5,  5,  4,  4,  4,  4},
+	{11,  9,  7,  7,  7,  6},
+	{ 2,  2,  2,  2,  2,  2},
+	{ 4,  4,  4,  4,  4,  4}
+};
+
+static const struct udmaspec udmaspec132 = {
+	{15, 10,  6,  7,  2,  3},
+	{31, 21, 12, 12,  5,  6},
+	{ 2,  2,  1,  1,  0,  1},
+	{ 1,  1,  1,  1,  0,  1},
+	{10,  7,  5,  3,  1,  1},
+	{ 1,  1,  1,  1,  1,  1},
+	{30, 27, 23, 15, 16, 12},
+	{20, 20, 20, 13, 14, 10},
+	{ 3,  3,  3,  3,  2,  3},
+	{ 2,  2,  2,  2,  1,  2},
+	{ 3,  3,  3,  3,  2,  3},
+	{ 3,  3,  3,  3,  2,  3},
+	{ 7,  4,  3,  3,  2,  3},
+	{10, 10,  8,  8,  7,  7},
+	{22, 17, 14, 14, 13, 12},
+	{ 3,  3,  3,  3,  2,  3},
+	{ 7,  7,  7,  7,  6,  7},
+};
+
+// -----------------------------------------------------------------------------------------------
 
 /* Bit definitions inside the registers */
 #define MPC52xx_ATA_HOSTCONF_SMR	0x80000000UL /* State machine reset */
@@ -75,6 +193,8 @@
 #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 {
@@ -133,6 +253,9 @@
 	u8  reserved21[2];
 };
 
+/* BestComm locking */
+unsigned long pata_mpc52xx_ata_dma_lock = 0;
+struct bcom_task_2 *pata_mpc52xx_ata_dma_task;
 
 /* ======================================================================== */
 /* Aux fns                                                                  */
@@ -141,6 +264,19 @@
 
 /* MPC52xx low level hw control */
 
+static inline void
+mpc52xx_ata_wait_tip_bit_clear(struct mpc52xx_ata __iomem *regs)
+{
+	int timeout = 1000;
+
+	while (in_be32(&regs->host_status) & MPC52xx_ATA_HOSTSTAT_TIP)
+		if (timeout-- == 0) {
+			printk(KERN_ERR "mpc52xx-ide: Timeout waiting for TIP clear\n");
+			break;
+		}
+	udelay(10);     /* FIXME: Necessary ??? */
+}
+
 static int
 mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio)
 {
@@ -165,6 +301,95 @@
 	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];
+	u32 t0M, td, tkw, tm, th, tj, tn;
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	t0M = priv->mdmaspec->t0M[speed];
+	td = priv->mdmaspec->td[speed];
+	tkw = priv->mdmaspec->tkw[speed];
+	tm = priv->mdmaspec->tm[speed];
+	th = priv->mdmaspec->th[speed];
+	tj = priv->mdmaspec->tj[speed];
+	tn = priv->mdmaspec->tn[speed];
+
+	DPRINTK ("t0M = %d\n", t0M);
+	DPRINTK ("td  = %d\n", td);
+	DPRINTK ("tkw = %d\n", tkw);
+	DPRINTK ("tm  = %d\n", tm);
+	DPRINTK ("th  = %d\n", th);
+	DPRINTK ("tj  = %d\n", tj);
+	DPRINTK ("tn  = %d\n", tn);
+
+	timing->mdma1 = (t0M << 24) | (td << 16) | (tkw << 8) | (tm);
+	timing->mdma2 = (th << 24) | (tj << 16) | (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];
+	u32 t2cyc, tcyc, tds, tdh, tdvs, tdvh, tfs, tli, tmli, taz, tenv, tsr, tss, trfs, trp, tack, tzah;
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	t2cyc = priv->udmaspec->t2cyc[speed];
+	tcyc = priv->udmaspec->tcyc[speed];
+	tds = priv->udmaspec->tds[speed];
+	tdh = priv->udmaspec->tdh[speed];
+	tdvs = priv->udmaspec->tdvs[speed];
+	tdvh = priv->udmaspec->tdvh[speed];
+	tfs = priv->udmaspec->tfs_min[speed];
+	tmli = priv->udmaspec->tmli[speed];
+	tenv = priv->udmaspec->tenv_min[speed];
+	tss = priv->udmaspec->tss[speed];
+	trp = priv->udmaspec->trp[speed];
+	tack = priv->udmaspec->tack[speed];
+	tzah = priv->udmaspec->tzah[speed];
+	taz = priv->udmaspec->taz[speed];
+	trfs = priv->udmaspec->trfs[speed];
+	tsr = priv->udmaspec->tsr[speed];
+	tli = priv->udmaspec->tli_max[speed];
+
+	DPRINTK ("UDMA t2cyc = %d\n", t2cyc);
+	DPRINTK ("UDMA tcyc  = %d\n", tcyc);
+	DPRINTK ("UDMA tds   = %d\n", tds);
+	DPRINTK ("UDMA tdh   = %d\n", tdh);
+	DPRINTK ("UDMA tdvs  = %d\n", tdvs);
+	DPRINTK ("UDMA tdvh  = %d\n", tdvh);
+	DPRINTK ("UDMA tfs   = %d\n", tfs);
+	DPRINTK ("UDMA tli   = %d\n", tli);
+	DPRINTK ("UDMA tmli  = %d\n", tmli);
+	DPRINTK ("UDMA taz   = %d\n", taz);
+	DPRINTK ("UDMA tenv  = %d\n", tenv);
+	DPRINTK ("UDMA tsr   = %d\n", tsr);
+	DPRINTK ("UDMA tss   = %d\n", tss);
+	DPRINTK ("UDMA trfs  = %d\n", trfs);
+	DPRINTK ("UDMA trp   = %d\n", trp);
+	DPRINTK ("UDMA tack  = %d\n", tack);
+	DPRINTK ("UDMA tzah  = %d\n", tzah);
+
+	timing->udma1 = (t2cyc << 24) | (tcyc << 16) | (tds << 8) | (tdh);
+	timing->udma2 = (tdvs << 24) | (tdvh << 16) | (tfs << 8) | (tli);
+	timing->udma3 = (tmli << 24) | (taz << 16) | (tenv << 8) | (tsr);
+	timing->udma4 = (tss << 24) | (trfs << 16) | (trp << 8) | (tack);
+	timing->udma5 = (tzah << 24);
+
+	timing->using_udma = 1;
+
+	return 0;
+}
+
 static void
 mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
 {
@@ -173,14 +398,13 @@
 
 	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 +469,28 @@
 	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) {
+		printk(KERN_ERR DRV_NAME
+			": 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 +501,192 @@
 	ata_sff_dev_select(ap,device);
 }
 
+static void
+mpc52xx_sff_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	mpc52xx_ata_wait_tip_bit_clear(priv->ata_regs);
+	ata_sff_exec_command(ap, tf);
+}
+
+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;
+	struct mpc52xx_ata __iomem *regs_pa = priv->ata_regs_pa;
+	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) {
+		u32 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_2((struct bcom_task_2 *) priv->dmatsk);
+
+			if (read) {
+				bd->status = tc;
+				bd->src_pa = (__force u32) &regs_pa->fifo_data;
+				bd->dst_pa = cur_addr;
+
+				invalidate_dcache_range((__force u32) phys_to_virt(cur_addr), (__force u32) phys_to_virt(cur_addr) + (__force u32) cur_len);
+			} else {
+				bd->status = tc;
+				bd->src_pa = cur_addr;
+				bd->dst_pa = (__force u32) &regs_pa->fifo_data;
+
+				flush_dcache_range((__force u32) phys_to_virt(cur_addr), (__force u32) phys_to_virt(cur_addr) + (__force u32) cur_len);
+			}
+
+			bcom_submit_next_buffer_2((struct bcom_task_2 *) priv->dmatsk, NULL);
+
+			cur_addr += tc;
+			cur_len -= tc;
+			count++;
+
+			if (count == MAX_DMA_BUFFERS) {
+				printk(KERN_ALERT "%s: %i dma table too small\n", __func__, __LINE__);
+				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)) {
+		printk(KERN_ALERT "%s: %i, return 1?\n", __func__, __LINE__);
+	}
+
+	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;
+			mpc52xx_ata_wait_tip_bit_clear(regs);
+
+			/* Configure FIFO with granularity to 7 like sample code */
+			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;
+			mpc52xx_ata_wait_tip_bit_clear(regs);
+
+			/* Configure FIFO with granularity to 4 like sample code */
+			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;
+
+	mpc52xx_ata_wait_tip_bit_clear(regs);
+	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;
+
+	/* LocalBus lock */
+	while (test_and_set_bit(0, &pata_mpc52xx_ata_dma_lock) != 0)
+		;
+
+	bcom_set_task_auto_start(priv->dmatsk->tasknum, priv->dmatsk->tasknum | 0x40);
+	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);
+
+	/* LocalBus unlock*/
+	clear_bit(0, &pata_mpc52xx_ata_dma_lock);
+
+	priv->waiting_for_dma = 0;
+}
+
+static u8
+mpc52xx_bmdma_status(struct ata_port *ap)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	return priv->waiting_for_dma;
+}
+
+static irqreturn_t
+mpc52xx_ata_task_irq(int irq, void *vpriv)
+{
+	struct mpc52xx_ata_priv *priv = vpriv;
+	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_exec_command	= mpc52xx_sff_exec_command,
+	.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 +703,14 @@
 
 	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		= 0x07;		/* Up to MWDMA2 */
+	ap->udma_mask		= ATA_UDMA2;	/* Up to UDMA2 */
+#else
+	ap->mwdma_mask		= 0x00;		/* No MWDMA */
+	ap->udma_mask		= 0x00;		/* No UDMA */
+#endif
 	ap->ops			= &mpc52xx_ata_port_ops;
 	host->private_data	= priv;
 
@@ -333,7 +760,7 @@
 	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 +816,31 @@
 
 	priv->ipb_period = 1000000000 / (ipb_freq / 1000);
 	priv->ata_regs = ata_regs;
+	priv->ata_regs_pa = (struct mpc52xx_ata __iomem *) 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);
+	pata_mpc52xx_ata_dma_task = (struct bcom_task_2 *) priv->dmatsk;
+	if (!priv->dmatsk) {
+		printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+		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)
+		printk(KERN_ALERT "%s: request_irq failed with: %i\n", __func__, ret);
 
 	/* Init the hw */
 	rv = mpc52xx_ata_hw_init(priv);

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2009-07-09 14:24 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <AclPDG6qSkF9cHawTOKR6zBfDIjvgg==>
2008-11-25 14:45 ` AW: [PATCH]: [MPC5200] Add ATA DMA support Lehmann, Hans (Ritter Elektronik)
2008-11-25 15:19   ` Matt Sealey
2008-11-25 15:35     ` AW: " Lehmann, Hans (Ritter Elektronik)
2008-12-20  7:15     ` Grant Likely
2008-12-20  7:18   ` Grant Likely
2009-01-05  9:25     ` AW: " Lehmann, Hans (Ritter Elektronik)
2009-01-05 15:31       ` Grant Likely
2009-07-09 14:24         ` Roman Fietze
2008-09-05 18:29 WITTROCK
     [not found] <DD39B5C3F4963040ADC9768BE7E430CB03202F4F@is-hdq-exchange.marel.net>
     [not found] ` <DD39B5C3F4963040ADC9768BE7E430CB03203863@is-hdq-exchange.marel.net>
     [not found]   ` <792f5f410808111319m20d6e09bi8e9782da1fdd4aab@mail.gmail.com>
     [not found]     ` <DD39B5C3F4963040ADC9768BE7E430CB0320387D@is-hdq-exchange.marel.net>
     [not found]       ` <792f5f410808111431g8fafba1n3b10468da0d165bf@mail.gmail.com>
     [not found]         ` <DD39B5C3F4963040ADC9768BE7E430CB03203A7B@is-hdq-exchange.marel.net>
     [not found]           ` <792f5f410808120643md72e679o39692c4bfa3f285e@mail.gmail.com>
     [not found]             ` <DD39B5C3F4963040ADC9768BE7E430CB03203A94@is-hdq-exchange.marel.net>
     [not found]               ` <792f5f410808120730j7b4be4f2n7a40de880178ccca@mail.gmail.com>
     [not found]                 ` <DD39B5C3F4963040ADC9768BE7E430CB03203ADC@is-hdq-exchange.marel.net>
     [not found]                   ` <792f5f410808120830v311e0446kc8fddfb97d0b9ea6@mail.gmail.com>
2008-08-12 17:30                     ` Daniel Schnell
2008-08-13  5:57                       ` Tim Yamin
2008-08-13  6:02                         ` Grant Likely
2008-08-13  6:06                           ` Tim Yamin
2008-08-13  6:11                             ` Grant Likely
2008-08-13  9:07                               ` Tim Yamin
2008-09-15  5:54                                 ` Grant Likely
2008-10-29 15:34                                   ` Matt Sealey
2008-11-11 14:04                                 ` Lehmann, Hans (Ritter Elektronik)
2008-11-11 14:35                                   ` Matt Sealey
  -- strict thread matches above, loose matches on Subject: below --
2008-06-17  8:28 Tim Yamin
2008-08-06 11:58 ` Daniel Schnell
2008-08-06 12:18   ` Tim Yamin
2008-08-06 13:26     ` Daniel Schnell
2008-08-06 14:31       ` Daniel Schnell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).