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< 65MHz */ #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 */