diff --git a/firmware/SOURCES b/firmware/SOURCES index 1a3a85f4fb..4e40d11cf7 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -343,6 +343,7 @@ target/arm/as3525/kernel-as3525.c target/arm/as3525/ata_sd_as3525.c target/arm/as3525/power-as3525.c target/arm/as3525/usb-as3525.c +target/arm/as3525/dma-pl081.c #ifndef BOOTLOADER target/arm/adc-as3514.c target/arm/as3525/pcm-as3525.c diff --git a/firmware/export/pl081.h b/firmware/export/pl081.h new file mode 100644 index 0000000000..3a75104ab6 --- /dev/null +++ b/firmware/export/pl081.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +/* Note: since the base address is not specified, you need to define DMAC_BASE + * before including this file */ + +/* ARM PrimeCell PL081 Single Master DMA controller */ + +#define DMAC_INT_STATUS (*(volatile unsigned long*)(DMAC_BASE+0x000)) +#define DMAC_INT_TC_STATUS (*(volatile unsigned long*)(DMAC_BASE+0x004)) +#define DMAC_INT_TC_CLEAR (*(volatile unsigned long*)(DMAC_BASE+0x008)) +#define DMAC_INT_ERROR_STATUS (*(volatile unsigned long*)(DMAC_BASE+0x00C)) +#define DMAC_INT_ERR_CLEAR (*(volatile unsigned long*)(DMAC_BASE+0x010)) +#define DMAC_RAW_INT_TC_STATUS (*(volatile unsigned long*)(DMAC_BASE+0x014)) +#define DMAC_RAW_INT_ERROR_STATUS (*(volatile unsigned long*)(DMAC_BASE+0x018)) +#define DMAC_ENBLD_CHANS (*(volatile unsigned long*)(DMAC_BASE+0x01C)) +#define DMAC_SOFT_B_REQ (*(volatile unsigned long*)(DMAC_BASE+0x020)) +#define DMAC_SOFT_S_REQ (*(volatile unsigned long*)(DMAC_BASE+0x024)) +#define DMAC_SOFT_LB_REQ (*(volatile unsigned long*)(DMAC_BASE+0x028)) +#define DMAC_SOFT_LS_REQ (*(volatile unsigned long*)(DMAC_BASE+0x02C)) +#define DMAC_CONFIGURATION (*(volatile unsigned long*)(DMAC_BASE+0x030)) +#define DMAC_SYNC (*(volatile unsigned long*)(DMAC_BASE+0x034)) + +/* Channel registers (0 & 1) */ +#define DMAC_CH_SRC_ADDR(c) (*(volatile unsigned long*)(DMAC_BASE+0x100+(0x20*c))) +#define DMAC_CH_DST_ADDR(c) (*(volatile unsigned long*)(DMAC_BASE+0x104+(0x20*c))) +#define DMAC_CH_LLI(c) (*(volatile unsigned long*)(DMAC_BASE+0x108+(0x20*c))) +#define DMAC_CH_CONTROL(c) (*(volatile unsigned long*)(DMAC_BASE+0x10C+(0x20*c))) +#define DMAC_CH_CONFIGURATION(c) (*(volatile unsigned long*)(DMAC_BASE+0x110+(0x20*c))) + +/* Test registers */ +#define DMAC_ITCR (*(volatile unsigned long*)(DMAC_BASE+0x500)) +#define DMAC_ITOP1 (*(volatile unsigned long*)(DMAC_BASE+0x504)) +#define DMAC_ITOP2 (*(volatile unsigned long*)(DMAC_BASE+0x508)) +#define DMAC_ITOP3 (*(volatile unsigned long*)(DMAC_BASE+0x50C)) + +/* Flow controllers */ + +/* Controller is DMAC */ +#define DMAC_FLOWCTRL_DMAC_MEM_TO_MEM 0 +#define DMAC_FLOWCTRL_DMAC_MEM_TO_PERI 1 +#define DMAC_FLOWCTRL_DMAC_PERI_TO_MEM 2 +#define DMAC_FLOWCTRL_DMAC_PERI_TO_PERI 3 + +/* Controller is peripheral */ +#define DMAC_FLOWCTRL_SRC_PERI_PERI_TO_PERI 4 +#define DMAC_FLOWCTRL_PERI_MEM_TO_PERI 5 +#define DMAC_FLOWCTRL_PERI_PERI_TO_MEM 6 +#define DMAC_FLOWCTRL_DST_PERI_PERI_TO_PERI 7 + +/* Transfer request sizes */ +#define DMA_S1 0 +#define DMA_S4 1 +#define DMA_S8 2 +#define DMA_S16 3 +#define DMA_S32 4 +#define DMA_S64 5 +#define DMA_S128 6 +#define DMA_S256 7 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 #include #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(!dma_finished) + yield(); - 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]; - } - } - - 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 +#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< ) \___| < | \_\ ( <_> > < < + * 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 +#include + +/* 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)