summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2008-11-25 13:38:32 +0000
committerRafaël Carré <rafael.carre@gmail.com>2008-11-25 13:38:32 +0000
commitc1f90b1881759376b4dcc6541fcb5c169ed7004f (patch)
treef7a4f4199695b45a3145fa13484d2ba97679fed0 /firmware/target
parentd7e4e54bcb03b19411caa1329795c3a1db608478 (diff)
Sansa AMS: Use DMA for SD transfers (read and write)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19211 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/as3525/ata_sd_as3525.c186
-rw-r--r--firmware/target/arm/as3525/dma-pl081.c96
-rw-r--r--firmware/target/arm/as3525/dma-target.h39
-rw-r--r--firmware/target/arm/as3525/system-as3525.c13
4 files changed, 215 insertions, 119 deletions
diff --git a/firmware/target/arm/as3525/ata_sd_as3525.c b/firmware/target/arm/as3525/ata_sd_as3525.c
index 1b34e21f05..2437faf76c 100644
--- a/firmware/target/arm/as3525/ata_sd_as3525.c
+++ b/firmware/target/arm/as3525/ata_sd_as3525.c
@@ -32,7 +32,9 @@
#include <stdlib.h>
#include <string.h>
#include "as3525.h"
-#include "pl180.h"
+#include "pl180.h" /* SD controller */
+#include "pl081.h" /* DMA controller */
+#include "dma-target.h" /* DMA request lines */
#include "panic.h"
#include "stdbool.h"
#include "ata_idle_notify.h"
@@ -71,8 +73,8 @@
#define MCI_FIFO(i) ((unsigned long *) (pl180_base[i]+0x80))
/* volumes */
-#define NAND_AS3525 0 /* embedded SD card */
-#define SD_AS3525 1 /* SD slot if present */
+#define INTERNAL_AS3525 0 /* embedded SD card */
+#define SD_SLOT_AS3525 1 /* SD slot if present */
static const int pl180_base[NUM_VOLUMES] = {
NAND_FLASH_BASE
@@ -81,6 +83,7 @@ static const int pl180_base[NUM_VOLUMES] = {
#endif
};
+/* TODO : BLOCK_SIZE != SECTOR_SIZE ? */
#define BLOCK_SIZE 512
#define SECTOR_SIZE 512
@@ -389,8 +392,8 @@ int sd_init(void)
CGU_PERI |= CGU_MCI_CLOCK_ENABLE;
#endif
- init_pl180_controller(NAND_AS3525);
- ret = sd_init_card(NAND_AS3525);
+ init_pl180_controller(INTERNAL_AS3525);
+ ret = sd_init_card(INTERNAL_AS3525);
if(ret < 0)
return ret;
@@ -398,8 +401,8 @@ int sd_init(void)
CCU_IO &= ~8; /* bits 3:2 = 01, xpd is SD interface */
CCU_IO |= 4;
- init_pl180_controller(SD_AS3525);
- sd_init_card(SD_AS3525);
+ init_pl180_controller(SD_SLOT_AS3525);
+ sd_init_card(SD_SLOT_AS3525);
#endif
queue_init(&sd_queue, true);
@@ -441,40 +444,6 @@ bool sd_present(IF_MV_NONVOID(int drive))
}
#endif
-int sd_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf)
-{
- (void)start;
- (void)count;
- (void)buf;
- return -1; /* TODO */
-}
-
-static int sd_poll_status(const int drive, unsigned int trigger, long timeout)
-{
- long t = current_tick;
- //int my_next_yield =0;
- int status;
-
- while (((status = MCI_STATUS(drive)) & trigger) == 0)
- {
- long time = current_tick;
-
-/*
- if (TIME_AFTER(time, my_next_yield))
- {
- long ty = current_tick;
- yield();
- timeout += current_tick - ty;
- my_next_yield = ty + MIN_YIELD_PERIOD;
- }
-*/
- if (TIME_AFTER(time, t + timeout))
- break;
- }
-
- return status;
-}
-
static int sd_wait_for_state(const int drive, unsigned int state)
{
unsigned int response = 0;
@@ -484,7 +453,7 @@ static int sd_wait_for_state(const int drive, unsigned int state)
while (1)
{
- long us;
+ long tick;
if(!send_cmd(drive, SD_SEND_STATUS, card_info[drive].rca,
MCI_RESP|MCI_ARG, &response))
@@ -496,35 +465,30 @@ static int sd_wait_for_state(const int drive, unsigned int state)
if(TIME_AFTER(current_tick, t + timeout))
return -1;
- us = current_tick;
- if (TIME_AFTER(us, next_yield))
+ if (TIME_AFTER((tick = current_tick), next_yield))
{
yield();
- timeout += current_tick - us;
- next_yield = us + MIN_YIELD_PERIOD;
+ timeout += current_tick - tick;
+ next_yield = tick + MIN_YIELD_PERIOD;
}
}
}
-int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
- void* inbuf)
+static int sd_transfer_sectors(IF_MV2(int drive,) unsigned long start,
+ int count, void* buf, bool write)
{
#ifndef HAVE_MULTIVOLUME
const int drive = 0;
#endif
int ret;
- unsigned char *buf_end, *buf = inbuf;
- int remaining = incount;
- const unsigned long *fifo_base = MCI_FIFO(drive);
/* skip SanDisk OF */
- if (drive == NAND_AS3525)
+ if (drive == INTERNAL_AS3525)
#if defined(SANSA_E200V2) || defined(SANSA_FUZE)
start += 61440;
#else
start += 20480;
#endif
- /* TODO: Add DMA support. */
mutex_lock(&sd_mtx);
@@ -533,15 +497,15 @@ int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
{
/* no external sd-card inserted */
ret = -88;
- goto sd_read_error;
+ goto sd_transfer_error;
}
#endif
if (card_info[drive].initialized < 0)
{
ret = card_info[drive].initialized;
- panicf("card not initalised");
- goto sd_read_error;
+ panicf("card not initialised");
+ goto sd_transfer_error;
}
last_disk_activity = current_tick;
@@ -550,109 +514,103 @@ int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
if (ret < 0)
{
panicf("wait for state failed");
- goto sd_read_error;
+ goto sd_transfer_error;
}
- disable_irq(); /* FIXME: data transfer is too slow and error prone when
- * interrupts are enabled */
-
- while(remaining)
+ while(count)
{
/* 128 * 512 = 2^16, and doesn't fit in the 16 bits of DATA_LENGTH
* register, so we have to transfer maximum 127 sectors at a time. */
- int transfer = (remaining >= 128) ? 127 : remaining; /* sectors */
+ unsigned int transfer = (count >= 128) ? 127 : count; /* sectors */
+ const int cmd =
+ write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
if(card_info[drive].ocr & (1<<30) ) /* SDHC */
- ret = send_cmd(drive, SD_READ_MULTIPLE_BLOCK, start, MCI_ARG, NULL);
+ ret = send_cmd(drive, cmd, start, MCI_ARG, NULL);
else
- ret = send_cmd(drive, SD_READ_MULTIPLE_BLOCK, start * BLOCK_SIZE,
+ ret = send_cmd(drive, cmd, start * BLOCK_SIZE,
MCI_ARG, NULL);
if (ret < 0)
{
- panicf("read multiple blocks failed");
- goto sd_read_error;
+ panicf("transfer multiple blocks failed");
+ goto sd_transfer_error;
}
- /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */
+ if(write)
+ dma_enable_channel(0, buf, MCI_FIFO(drive),
+ (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT,
+ DMAC_FLOWCTRL_PERI_MEM_TO_PERI, true, false, 0, DMA_S8);
+ else
+ dma_enable_channel(0, MCI_FIFO(drive), buf,
+ (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT,
+ DMAC_FLOWCTRL_PERI_PERI_TO_MEM, false, true, 0, DMA_S8);
MCI_DATA_TIMER(drive) = 0x1000000; /* FIXME: arbitrary */
MCI_DATA_LENGTH(drive) = transfer * card_info[drive].block_size;
- MCI_DATA_CTRL(drive) = (1<<0) /* enable */ |
- (1<<1) /* from card to controller */ |
+ MCI_DATA_CTRL(drive) = (1<<0) /* enable */ |
+ (!write<<1) /* transfer direction */ |
+ (1<<3) /* DMA */ |
(9<<4) /* 2^9 = 512 */ ;
- buf_end = buf + transfer * card_info[drive].block_size;
-
- while(buf < buf_end)
- {
- /* Wait for the FIFO to be half full */
- const int trigger = MCI_RX_FIFO_HALF_FULL|MCI_RX_FIFO_FULL;
- int controller_status = sd_poll_status(drive, trigger, 100);
-
- controller_status &= ~(MCI_RX_ACTIVE|MCI_RX_DATA_AVAIL|
- MCI_DATA_BLOCK_END|MCI_DATA_END);
-
- if(!controller_status || (controller_status & ~trigger))
- panicf("incorrect status 0x%x", controller_status & ~trigger);
-
- if(((intptr_t)buf & 3) == 0)
- { /* aligned destination buffer */
- asm volatile(
- "ldmia %2, {r0-r7} \n" /* load 8 * 4 bytes */
- "stmia %1!, {r0-r7} \n" /* store 8 * 4 bytes */
- :"=r"(buf) /* output */
- :"r"(buf), "r"(fifo_base) /* input */
- :"r0","r1","r2","r3","r4","r5","r6","r7" /* clobbers */
- );
- }
- else
- { /* non aligned destination buffer */
- int tmp[8];
- asm volatile(
- "ldmia %1, {r0-r7} \n" /* load 8 * 4 bytes */
- "stmia %0, {r0-r7} \n" /* store 8 * 4 bytes */
- :/* no output */
- :"r"(tmp), "r"(fifo_base) /* input */
- :"r0","r1","r2","r3","r4","r5","r6","r7" /* clobbers */
- );
- memcpy(buf, tmp, 32);
- buf = &buf[32];
- }
- }
+ while(!dma_finished)
+ yield();
- remaining -= transfer;
+ buf += transfer * SECTOR_SIZE;
start += transfer;
+ count -= transfer;
last_disk_activity = current_tick;
if(!send_cmd(drive, SD_STOP_TRANSMISSION, 0, MCI_NO_FLAGS, NULL))
{
ret = -666;
panicf("STOP TRANSMISSION failed");
- goto sd_read_error;
+ goto sd_transfer_error;
}
ret = sd_wait_for_state(drive, SD_TRAN);
if (ret < 0)
{
panicf(" wait for state TRAN failed");
- goto sd_read_error;
+ goto sd_transfer_error;
}
}
+
while (1)
{
mutex_unlock(&sd_mtx);
- enable_irq();
-
return ret;
-sd_read_error:
- panicf("read error : %d",ret);
+sd_transfer_error:
+ panicf("transfer error : %d",ret);
card_info[drive].initialized = 0;
}
}
+int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int count,
+ void* buf)
+{
+ return sd_transfer_sectors(IF_MV2(drive,) start, count, buf, false);
+}
+
+int sd_write_sectors(IF_MV2(int drive,) unsigned long start, int count,
+ const void* buf)
+{
+
+#ifdef BOOTLOADER /* we don't need write support in bootloader */
+#ifdef HAVE_MULTIVOLUME
+ (void) drive;
+#endif
+ (void) start;
+ (void) count;
+ (void) buf;
+ return -1;
+#else
+ return sd_transfer_sectors(IF_MV2(drive,) start, count, (void*)buf, true);
+#endif
+}
+
#ifndef BOOTLOADER
void sd_sleep(void)
{
diff --git a/firmware/target/arm/as3525/dma-pl081.c b/firmware/target/arm/as3525/dma-pl081.c
new file mode 100644
index 0000000000..31021e1560
--- /dev/null
+++ b/firmware/target/arm/as3525/dma-pl081.c
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright © 2008 Rafaël Carré
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "as3525.h"
+#include "pl081.h"
+#include "dma-target.h"
+#include <stdbool.h>
+#include "panic.h"
+
+volatile bool dma_finished;
+
+void dma_init(void)
+{
+ /* Enable DMA controller */
+ CGU_PERI |= CGU_DMA_CLOCK_ENABLE;
+ DMAC_CONFIGURATION |= (1<<0);
+ DMAC_SYNC = 0;
+ VIC_INT_ENABLE |= INTERRUPT_DMAC;
+}
+
+void dma_enable_channel(int channel, void *src, void *dst, int peri,
+ int flow_controller, bool src_inc, bool dst_inc,
+ size_t size, int nwords)
+{
+ int control = 0;
+
+ DMAC_CH_SRC_ADDR(channel) = (int)src;
+ DMAC_CH_DST_ADDR(channel) = (int)dst;
+
+ DMAC_CH_LLI(channel) = 0; /* we use contigous memory, so don't use the LLI */
+
+ /* specify address increment */
+ if(src_inc)
+ control |= (1<<26);
+
+ if(dst_inc)
+ control |= (1<<27);
+
+ /* OF use transfers of 4 * 32 bits words on memory, i2sin, i2sout */
+ /* OF use transfers of 8 * 32 bits words on SD */
+
+ control |= (2<<21) | (2<<18); /* dst/src width = word, 32bit */
+ control |= (nwords<<15) | (nwords<<12); /* dst/src size */
+ control |= (size & 0x7ff); /* transfer size */
+
+ control |= (1<<31); /* current LLI is expected to trigger terminal count interrupt */
+
+ DMAC_CH_CONTROL(channel) = control;
+
+ dma_finished = false;
+
+ /* only needed if DMAC and Peripheral do not run at the same clock speed */
+ DMAC_SYNC |= (1<<peri);
+
+ /* we set the same peripheral as source and destination because we always
+ * use memory-to-peripheral or peripheral-to-memory transfers */
+ DMAC_CH_CONFIGURATION(channel) =
+ (flow_controller<<11) /* flow controller is peripheral */
+ | (1<<15) /* terminal count interrupt mask */
+ | (1<<14) /* interrupt error mask */
+ | (peri<<6) /* dst peripheral */
+ | (peri<<1) /* src peripheral */
+ | (1<<0) /* enable channel */
+ ;
+}
+
+/* isr */
+void INT_DMAC(void)
+{
+ int channel = (DMAC_INT_STATUS & (1<<0)) ? 0 : 1;
+
+ if(DMAC_INT_ERROR_STATUS & (1<<channel))
+ panicf("DMA error, channel %d", channel);
+
+ DMAC_INT_TC_CLEAR |= (1<<channel); /* clear terminal count interrupt */
+
+ dma_finished = true; /* TODO : use struct wakeup ? */
+}
diff --git a/firmware/target/arm/as3525/dma-target.h b/firmware/target/arm/as3525/dma-target.h
new file mode 100644
index 0000000000..74dff730ad
--- /dev/null
+++ b/firmware/target/arm/as3525/dma-target.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright © 2008 Rafaël Carré
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* DMA request lines (16 max): not specified in AS3525 datasheet, but common to
+ * all AS3525 based models (made by SanDisk) supported by rockbox. */
+
+#define DMA_PERI_SD_SLOT 2
+#define DMA_PERI_I2SOUT 3
+#define DMA_PERI_I2SIN 4
+#define DMA_PERI_SD 5 /* embedded storage */
+#define DMA_PERI_DBOP 8
+
+void dma_init(void);
+void dma_enable_channel(int channel, void *src, void *dst, int peri,
+ int flow_controller, bool src_inc, bool dst_inc,
+ size_t size, int nwords);
+
+extern volatile bool dma_finished;
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index d6b042d539..10338ee148 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -24,6 +24,7 @@
#include "system.h"
#include "panic.h"
#include "ascodec-target.h"
+#include "dma-target.h"
#define default_interrupt(name) \
extern __attribute__((weak,alias("UIRQ"))) void name (void)
@@ -174,7 +175,7 @@ static void sdram_init(void)
#elif defined(SANSA_E200V2) || defined(SANSA_FUZE)
/* 16 bits external bus, high performance SDRAM, 64 Mbits = 8 Mbytes */
#define MEMORY_MODEL 0x5
-
+
#else
#error "The external memory in your player is unknown"
#endif
@@ -220,9 +221,6 @@ void system_init(void)
CGU_PROC = (3<<2)|0x01; /* fclk = PLLA*5/8 = 240 MHz */
asm volatile(
- "mrs r0, cpsr \n"
- "orr r0, r0, #0x80 \n" /* disable interrupts */
- "msr cpsr, r0 \n"
"mov r0, #0 \n"
"mcr p15, 0, r0, c7, c7 \n" /* invalidate icache & dcache */
"mrc p15, 0, r0, c1, c0 \n" /* control register */
@@ -239,15 +237,20 @@ void system_init(void)
CGU_PERI |= CGU_TIMERIF_CLOCK_ENABLE;
/* enable VIC */
+ VIC_INT_ENABLE = 0; /* disable all interrupt lines */
CGU_PERI |= CGU_VIC_CLOCK_ENABLE;
VIC_INT_SELECT = 0; /* only IRQ, no FIQ */
+ enable_irq();
#else
- /* disable fast hardware power-off, to use power button normally */
+ /* Disable fast hardware power-off, to use power button normally
+ * We don't need the power button in the bootloader. */
ascodec_init();
ascodec_write(AS3514_CVDD_DCDC3, ascodec_read(AS3514_CVDD_DCDC3) & (1<<2));
#endif /* BOOTLOADER */
+
+ dma_init();
}
void system_reboot(void)