summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTorne Wuff <torne@wolfpuppy.org.uk>2010-01-31 11:07:29 +0000
committerTorne Wuff <torne@wolfpuppy.org.uk>2010-01-31 11:07:29 +0000
commit533cf7737bae4363edefbfb202cb44e9eb30cd3f (patch)
treeabfb69b76b46fc8d22c14023969ba3fcaad4809d
parentc0e2d9fe1bb87730d9a8a7cac3cb8f6cd39ce630 (diff)
downloadrockbox-533cf7737bae4363edefbfb202cb44e9eb30cd3f.tar.gz
rockbox-533cf7737bae4363edefbfb202cb44e9eb30cd3f.zip
Enable ATA DMA on pp5020 based players with ATA drives.
DMA is only used for reading because writing seems to be slower with DMA. Only requests which are cacheline aligned (16 bytes) will use DMA, so many requests will still use PIO at this point; a later change will align more reads. Part of FS#9708, original DMA code by Boris Gjenero (dreamlayers). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24405 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/export/config/gogearhdd1630.h5
-rw-r--r--firmware/export/config/ipod4g.h5
-rw-r--r--firmware/export/config/ipodcolor.h5
-rw-r--r--firmware/export/config/ipodmini1g.h5
-rw-r--r--firmware/export/config/ipodmini2g.h5
-rw-r--r--firmware/export/config/ipodnano1g.h5
-rw-r--r--firmware/export/config/ipodvideo.h4
-rw-r--r--firmware/export/config/iriverh10.h5
-rw-r--r--firmware/export/config/iriverh10_5gb.h5
-rw-r--r--firmware/export/config/mrobe100.h5
-rw-r--r--firmware/export/config/samsungyh820.h5
-rw-r--r--firmware/export/config/samsungyh920.h5
-rw-r--r--firmware/export/config/samsungyh925.h5
-rw-r--r--firmware/export/config/tatungtpj1022.h5
-rw-r--r--firmware/target/arm/ata-pp5020.c183
-rw-r--r--firmware/target/arm/ata-target.h34
16 files changed, 286 insertions, 0 deletions
diff --git a/firmware/export/config/gogearhdd1630.h b/firmware/export/config/gogearhdd1630.h
index 912ba427c0..8bb14801b0 100644
--- a/firmware/export/config/gogearhdd1630.h
+++ b/firmware/export/config/gogearhdd1630.h
@@ -200,3 +200,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipod4g.h b/firmware/export/config/ipod4g.h
index e6bdc35bd8..cd71434c10 100644
--- a/firmware/export/config/ipod4g.h
+++ b/firmware/export/config/ipod4g.h
@@ -207,3 +207,8 @@
#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipodcolor.h b/firmware/export/config/ipodcolor.h
index 0f1de4fdba..c0168d9886 100644
--- a/firmware/export/config/ipodcolor.h
+++ b/firmware/export/config/ipodcolor.h
@@ -182,3 +182,8 @@
#define IPOD_ACCESSORY_PROTOCOL
#define HAVE_SERIAL
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipodmini1g.h b/firmware/export/config/ipodmini1g.h
index 129829ffbb..3ab96e3a62 100644
--- a/firmware/export/config/ipodmini1g.h
+++ b/firmware/export/config/ipodmini1g.h
@@ -193,3 +193,8 @@
#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipodmini2g.h b/firmware/export/config/ipodmini2g.h
index 8087269485..72e04a30db 100644
--- a/firmware/export/config/ipodmini2g.h
+++ b/firmware/export/config/ipodmini2g.h
@@ -203,3 +203,8 @@
#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipodnano1g.h b/firmware/export/config/ipodnano1g.h
index 5f63c269ed..562c940df4 100644
--- a/firmware/export/config/ipodnano1g.h
+++ b/firmware/export/config/ipodnano1g.h
@@ -192,3 +192,8 @@
#define IPOD_ACCESSORY_PROTOCOL
#define HAVE_SERIAL
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/ipodvideo.h b/firmware/export/config/ipodvideo.h
index 9aa1d49547..d188696e64 100644
--- a/firmware/export/config/ipodvideo.h
+++ b/firmware/export/config/ipodvideo.h
@@ -224,3 +224,7 @@
#define IPOD_ACCESSORY_PROTOCOL
#define HAVE_SERIAL
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/iriverh10.h b/firmware/export/config/iriverh10.h
index cde1b6075e..5365c83239 100644
--- a/firmware/export/config/iriverh10.h
+++ b/firmware/export/config/iriverh10.h
@@ -186,3 +186,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/iriverh10_5gb.h b/firmware/export/config/iriverh10_5gb.h
index e69f6c2b20..bfc9b8ac2e 100644
--- a/firmware/export/config/iriverh10_5gb.h
+++ b/firmware/export/config/iriverh10_5gb.h
@@ -169,3 +169,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
#endif
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/mrobe100.h b/firmware/export/config/mrobe100.h
index 90419914b2..2bbb03919d 100644
--- a/firmware/export/config/mrobe100.h
+++ b/firmware/export/config/mrobe100.h
@@ -200,3 +200,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/samsungyh820.h b/firmware/export/config/samsungyh820.h
index 4968960803..0ca244ae39 100644
--- a/firmware/export/config/samsungyh820.h
+++ b/firmware/export/config/samsungyh820.h
@@ -181,3 +181,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/samsungyh920.h b/firmware/export/config/samsungyh920.h
index 310fa1374c..a6a57f7227 100644
--- a/firmware/export/config/samsungyh920.h
+++ b/firmware/export/config/samsungyh920.h
@@ -187,3 +187,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/samsungyh925.h b/firmware/export/config/samsungyh925.h
index 55d46ae1e1..c19901c019 100644
--- a/firmware/export/config/samsungyh925.h
+++ b/firmware/export/config/samsungyh925.h
@@ -185,3 +185,8 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/export/config/tatungtpj1022.h b/firmware/export/config/tatungtpj1022.h
index 079be73c86..aca131df8b 100644
--- a/firmware/export/config/tatungtpj1022.h
+++ b/firmware/export/config/tatungtpj1022.h
@@ -137,3 +137,8 @@
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
+
+/* DMA is used only for reading on PP502x because although reads are ~8x faster
+ * writes appear to be ~25% slower.
+ */
+#define HAVE_ATA_DMA
diff --git a/firmware/target/arm/ata-pp5020.c b/firmware/target/arm/ata-pp5020.c
index 8e2200c901..c8ce148dd7 100644
--- a/firmware/target/arm/ata-pp5020.c
+++ b/firmware/target/arm/ata-pp5020.c
@@ -48,6 +48,12 @@ void ata_device_init()
#ifdef SAMSUNG_YH920
CPU_INT_DIS = (1<<IDE_IRQ);
#endif
+#ifdef HAVE_ATA_DMA
+ IDE_DMA_CONTROL |= 2;
+ IDE_DMA_CONTROL &= ~1;
+ IDE0_CFG &= ~0x8010;
+ IDE0_CFG |= 0x20;
+#else
/* From ipod-ide.c:ipod_ide_register() */
IDE0_CFG |= (1<<5);
@@ -56,7 +62,184 @@ void ata_device_init()
#else
IDE0_CFG &=~(0x10000000); /* cpu < 65MHz */
#endif
+#endif
IDE0_PRI_TIMING0 = 0x10;
IDE0_PRI_TIMING1 = 0x80002150;
}
+
+/* These are PIO timings for 80 Mhz. At 24 Mhz,
+ the first value is 0 but the rest are the same.
+ They go in IDE0_PRI_TIMING0.
+
+ Rockbox used 0x10, and test_disk shows that leads to faster PIO.
+ If 0x10 is incorrect, these timings may be needed with some devices.
+static const unsigned long pio80mhz[] = {
+ 0xC293, 0x43A2, 0x11A1, 0x7232, 0x3131
+};
+*/
+
+#ifdef HAVE_ATA_DMA
+/* Timings for multi-word and ultra DMA modes.
+ These go in IDE0_PRI_TIMING1
+ */
+static const unsigned long tm_mwdma[] = {
+ 0xF9F92, 0x56562, 0x45451
+};
+
+static const unsigned long tm_udma[] = {
+ 0x800037C1, 0x80003491, 0x80003371,
+#if ATA_MAX_UDMA > 2
+ 0x80003271, 0x80003071
+#endif
+};
+
+#if ATA_MAX_UDMA > 2
+static bool dma_boosted = false;
+static bool dma_needs_boost;
+#endif
+
+/* This function sets up registers for 80 Mhz.
+ Ultra DMA mode 2 works at 30 Mhz.
+ */
+void ata_dma_set_mode(unsigned char mode) {
+ int modeidx;
+
+ (*(volatile unsigned long *)(0x600060C4)) = 0xC0000000; /* 80 Mhz */
+ IDE0_CFG &= ~0x10000000;
+
+ modeidx = mode & 7;
+ mode &= 0xF8;
+ if (mode == 0x40 && modeidx <= ATA_MAX_UDMA) {
+ IDE0_PRI_TIMING1 = tm_udma[modeidx];
+#if ATA_MAX_UDMA > 2
+ if (modeidx > 2)
+ dma_needs_boost = true;
+ else
+ dma_needs_boost = false;
+#endif
+ } else if (mode == 0x20 && modeidx <= ATA_MAX_MWDMA)
+ IDE0_PRI_TIMING1 = tm_mwdma[modeidx];
+
+ IDE0_CFG |= 0x20000000; /* >= 50 Mhz */
+}
+
+#define IDE_CFG_INTRQ 8
+#define IDE_DMA_CONTROL_READ 8
+
+/* This waits for an ATA interrupt using polling.
+ In ATA_CONTROL, CONTROL_nIEN must be cleared.
+ */
+STATICIRAM ICODE_ATTR int ata_wait_intrq(void)
+{
+ long timeout = current_tick + HZ*10;
+
+ do
+ {
+ if (IDE0_CFG & IDE_CFG_INTRQ)
+ return 1;
+ ata_keep_active();
+ yield();
+ } while (TIME_BEFORE(current_tick, timeout));
+
+ return 0; /* timeout */
+}
+
+/* This function checks if parameters are appropriate for DMA,
+ and if they are, it sets up for DMA.
+
+ If return value is false, caller may use PIO for this transfer.
+
+ If return value is true, caller must issue a DMA ATA command
+ and then call ata_dma_finish().
+ */
+bool ata_dma_setup(void *addr, unsigned long bytes, bool write) {
+ /* Require cacheline alignment for reads to prevent interference. */
+ if (!write && ((unsigned long)addr & 15))
+ return false;
+
+ /* Writes only need to be word-aligned, but by default DMA
+ * is not used for writing as it appears to be slower.
+ */
+#ifdef ATA_DMA_WRITES
+ if (write && ((unsigned long)addr & 3))
+ return false;
+#else
+ if (write)
+ return false;
+#endif
+
+#if ATA_MAX_UDMA > 2
+ if (dma_needs_boost && !dma_boosted) {
+ cpu_boost(true);
+ dma_boosted = true;
+ }
+#endif
+
+ if (write) {
+ /* If unflushed, old data may be written to disk */
+ cpucache_flush();
+ }
+ else {
+ /* Invalidate cache because new data may be present in RAM */
+ cpucache_invalidate();
+ }
+
+ /* Clear pending interrupts so ata_dma_finish() can wait for an
+ interrupt from this transfer
+ */
+ IDE0_CFG |= IDE_CFG_INTRQ;
+
+ IDE_DMA_CONTROL |= 2;
+ IDE_DMA_LENGTH = bytes - 4;
+
+#ifndef BOOTLOADER
+ if ((unsigned long)addr < DRAM_START)
+ /* Rockbox remaps DRAM to start at 0 */
+ IDE_DMA_ADDR = (unsigned long)addr + DRAM_START;
+ else
+#endif
+ IDE_DMA_ADDR = (unsigned long)addr;
+
+ if (write)
+ IDE_DMA_CONTROL &= ~IDE_DMA_CONTROL_READ;
+ else
+ IDE_DMA_CONTROL |= IDE_DMA_CONTROL_READ;
+
+ IDE0_CFG |= 0x8000;
+
+ return true;
+}
+
+/* This function waits for a DMA transfer to end.
+ It must be called to finish what ata_dma_setup started.
+
+ Return value is true if DMA completed before the timeout, and false
+ if a timeout happened.
+ */
+bool ata_dma_finish(void) {
+ bool res;
+
+ /* It may be okay to put this at the end of setup */
+ IDE_DMA_CONTROL |= 1;
+
+ /* Wait for end of transfer.
+ Reading standard ATA status while DMA is in progress causes
+ failures and hangs. Because of that, another wait is used.
+ */
+ res = ata_wait_intrq();
+
+ IDE0_CFG &= ~0x8000;
+ IDE_DMA_CONTROL &= ~0x80000001;
+
+#if ATA_MAX_UDMA > 2
+ if (dma_boosted) {
+ cpu_boost(false);
+ dma_boosted = false;
+ }
+#endif
+
+ return res;
+}
+
+#endif /* HAVE_ATA_DMA */
diff --git a/firmware/target/arm/ata-target.h b/firmware/target/arm/ata-target.h
index 0881aaef35..82c5a5f555 100644
--- a/firmware/target/arm/ata-target.h
+++ b/firmware/target/arm/ata-target.h
@@ -81,3 +81,37 @@ void copy_write_sectors(const unsigned char* buf, int wordcount);
void ata_reset(void);
bool ata_is_coldstart(void);
void ata_device_init(void);
+
+#ifdef HAVE_ATA_DMA
+
+/* IDE DMA controller registers */
+#define IDE_DMA_CONTROL (*(volatile unsigned long *)(0xc3000400))
+#define IDE_DMA_LENGTH (*(volatile unsigned long *)(0xc3000408))
+#define IDE_DMA_ADDR (*(volatile unsigned long *)(0xc300040C))
+
+/* Maximum multi-word DMA mode supported by the controller */
+#define ATA_MAX_MWDMA 2
+
+#ifndef BOOTLOADER
+/* The PP5020 supports UDMA 4, but it needs cpu boosting and only
+ * improves performance by ~10% with a stock disk.
+ * UDMA 2 is stable at 30 Mhz.
+ * UDMA 1 is stable at 24 Mhz.
+ */
+#if CPUFREQ_NORMAL >= 30000000
+#define ATA_MAX_UDMA 2
+#elif CPUFREQ_NORMAL >= 24000000
+#define ATA_MAX_UDMA 1
+#else
+#error "CPU speeds under 24Mhz have not been tested with DMA"
+#endif
+#else
+/* The bootloader runs at 24 Mhz and needs a slower mode */
+#define ATA_MAX_UDMA 1
+#endif
+
+void ata_dma_set_mode(unsigned char mode);
+bool ata_dma_setup(void *addr, unsigned long bytes, bool write);
+bool ata_dma_finish(void);
+
+#endif /* HAVE_ATA_DMA */