summaryrefslogtreecommitdiffstats
path: root/firmware/target
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/as3525/app.lds126
-rw-r--r--firmware/target/arm/as3525/ata_sd_as3525.c590
-rw-r--r--firmware/target/arm/as3525/kernel-as3525.c6
-rw-r--r--firmware/target/arm/as3525/sansa-clip/system-target.h4
-rw-r--r--firmware/target/arm/as3525/system-as3525.c17
5 files changed, 595 insertions, 148 deletions
diff --git a/firmware/target/arm/as3525/app.lds b/firmware/target/arm/as3525/app.lds
new file mode 100644
index 0000000000..08b119eb3f
--- /dev/null
+++ b/firmware/target/arm/as3525/app.lds
@@ -0,0 +1,126 @@
+#include "config.h"
+
+ENTRY(start)
+
+OUTPUT_FORMAT(elf32-littlearm)
+OUTPUT_ARCH(arm)
+STARTUP(target/arm/crt0.o)
+
+#define PLUGINSIZE PLUGIN_BUFFER_SIZE
+#define CODECSIZE CODEC_SIZE
+
+#ifdef DEBUG
+#define STUBOFFSET 0x10000
+#else
+#define STUBOFFSET 0
+#endif
+
+#include "cpu.h"
+#define IRAMSIZE 0x50000
+#define DRAMSIZE (MEMORYSIZE * 0x100000) - STUBOFFSET - PLUGINSIZE - CODECSIZE
+
+#define IRAMORIG 0x0
+#define DRAMORIG 0x30000000 + STUBOFFSET
+
+/* End of the audio buffer, where the codec buffer starts */
+#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE)
+
+/* Where the codec buffer ends, and the plugin buffer starts */
+#define ENDADDR (ENDAUDIOADDR + CODECSIZE)
+
+MEMORY
+{
+ IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
+ DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
+}
+
+SECTIONS
+{
+ loadaddress = 0x30000000;
+
+ .vectors DRAMORIG :
+ {
+ _vectorstart = .;
+ *(.vectors*);
+ *(.init.text)
+ . = ALIGN(0x4);
+ } > DRAM
+
+ .text :
+ {
+ _textstart = .;
+ *(.text)
+ *(.text*)
+ *(.icode)
+ *(.glue_7)
+ *(.glue_7t)
+ . = ALIGN(0x4);
+ } > DRAM
+
+ .rodata :
+ {
+ *(.rodata) /* problems without this, dunno why */
+ *(.rodata*)
+ *(.rodata.str1.1)
+ *(.rodata.str1.4)
+ *(.irodata*)
+ . = ALIGN(0x4);
+ } > DRAM
+
+ .data :
+ {
+ *(.data*)
+ *(.idata*)
+ . = ALIGN(0x4);
+ } > DRAM
+
+ /DISCARD/ :
+ {
+ *(.eh_frame)
+ }
+
+ _initdata_end =.;
+
+ .stack (NOLOAD) :
+ {
+ *(.stack)
+ stackbegin = .;
+ . += 0x2000;
+ stackend = .;
+ } > DRAM
+
+ .bss (NOLOAD) :
+ {
+ _edata = .;
+ *(.bss*)
+ *(.ibss*)
+ *(COMMON)
+ . = ALIGN(0x4);
+ _end = .;
+ } > DRAM
+
+ .audiobuf (NOLOAD) :
+ {
+ . = ALIGN(4);
+ _audiobuffer = .;
+ audiobuffer = .;
+ } > DRAM
+
+ .audiobufend ENDAUDIOADDR (NOLOAD) :
+ {
+ audiobufend = .;
+ _audiobufend = .;
+ } > DRAM
+
+ .codec ENDAUDIOADDR (NOLOAD) :
+ {
+ codecbuf = .;
+ _codecbuf = .;
+ }
+
+ .plugin ENDADDR (NOLOAD) :
+ {
+ _pluginbuf = .;
+ pluginbuf = .;
+ }
+}
diff --git a/firmware/target/arm/as3525/ata_sd_as3525.c b/firmware/target/arm/as3525/ata_sd_as3525.c
index 77d1ec1504..18cbb139e0 100644
--- a/firmware/target/arm/as3525/ata_sd_as3525.c
+++ b/firmware/target/arm/as3525/ata_sd_as3525.c
@@ -7,6 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
+ * Copyright (C) 2006 Daniel Ankers
* Copyright © 2008 Rafaël Carré
*
* This program is free software; you can redistribute it and/or
@@ -22,19 +23,31 @@
/* Driver for the ARM PL180 SD/MMC controller inside AS3525 SoC */
#include "config.h" /* for HAVE_MULTIVOLUME */
-
+#include "fat.h"
+#include "thread.h"
+#include "hotswap.h"
+#include "system.h"
+#include "cpu.h"
+#include <stdlib.h>
#include "as3525.h"
#include "pl180.h"
#include "panic.h"
#include "stdbool.h"
+#include "ata_idle_notify.h"
#include "sd.h"
-#define NAND_AS3525 0
-#define SD_AS3525 1
-static const int pl180_base[2] = { NAND_FLASH_BASE, SD_MCI_BASE };
+#ifdef HAVE_HOTSWAP
+#include "disk.h"
+#endif
+
+/* command flags */
+#define MMC_NO_FLAGS (0<<0)
+#define MMC_RESP (1<<0)
+#define MMC_LONG_RESP (1<<1)
+#define MMC_ARG (1<<2)
/* ARM PL180 registers */
-#define MMC_POWER(i) (*(volatile unsigned long *) (pl180_base[i]+0x00))
+#define MMC_POWER(i) (*(volatile unsigned char *) (pl180_base[i]+0x00))
#define MMC_CLOCK(i) (*(volatile unsigned long *) (pl180_base[i]+0x04))
#define MMC_ARGUMENT(i) (*(volatile unsigned long *) (pl180_base[i]+0x08))
#define MMC_COMMAND(i) (*(volatile unsigned long *) (pl180_base[i]+0x0C))
@@ -43,40 +56,45 @@ static const int pl180_base[2] = { NAND_FLASH_BASE, SD_MCI_BASE };
#define MMC_RESP1(i) (*(volatile unsigned long *) (pl180_base[i]+0x18))
#define MMC_RESP2(i) (*(volatile unsigned long *) (pl180_base[i]+0x1C))
#define MMC_RESP3(i) (*(volatile unsigned long *) (pl180_base[i]+0x20))
-#define MMC_DATACTRL(i) (*(volatile unsigned long *) (pl180_base[i]+0x2C))
+#define MMC_DATA_TIMER(i) (*(volatile unsigned long *) (pl180_base[i]+0x24))
+#define MMC_DATA_LENGTH(i) (*(volatile unsigned short*) (pl180_base[i]+0x28))
+#define MMC_DATA_CTRL(i) (*(volatile unsigned char *) (pl180_base[i]+0x2C))
+#define MMC_DATA_CNT(i) (*(volatile unsigned short*) (pl180_base[i]+0x30))
#define MMC_STATUS(i) (*(volatile unsigned long *) (pl180_base[i]+0x34))
#define MMC_CLEAR(i) (*(volatile unsigned long *) (pl180_base[i]+0x38))
#define MMC_MASK0(i) (*(volatile unsigned long *) (pl180_base[i]+0x3C))
#define MMC_MASK1(i) (*(volatile unsigned long *) (pl180_base[i]+0x40))
#define MMC_SELECT(i) (*(volatile unsigned long *) (pl180_base[i]+0x44))
+#define MMC_FIFO_CNT(i) (*(volatile unsigned long *) (pl180_base[i]+0x48))
+#define MMC_FIFO(i) ((unsigned long *) (pl180_base[i]+0x80))
+/* volumes */
+#define NAND_AS3525 0
+#define SD_AS3525 1
-/* SD commands */
-#define GO_IDLE_STATE 0
-#define MMC_CMD_READ_CID 2
-#define SEND_IF_COND 8
-#define SEND_OP_COND 41
-#define APP_CMD 55
+static const int pl180_base[NUM_VOLUMES] = {
+ NAND_FLASH_BASE
+#ifdef HAVE_MULTIVOLUME
+ , SD_MCI_BASE
+#endif
+};
-/* command flags */
-#define MMC_NO_FLAGS (0<<0)
-#define MMC_RESP (1<<0)
-#define MMC_LONG_RESP (1<<1)
-#define MMC_ARG (1<<2)
+#define BLOCK_SIZE 512
+#define SECTOR_SIZE 512
-#ifdef BOOTLOADER
-#define DEBUG
-void reset_screen(void);
-void printf(const char *format, ...);
-#endif
+static tSDCardInfo card_info[NUM_VOLUMES];
-struct mmc_command
-{
- int cmd;
- int arg;
- int resp[4];
- int flags;
-};
+/* for compatibility */
+static long last_disk_activity = -1;
+
+#define MIN_YIELD_PERIOD 1000
+static long next_yield = 0;
+
+/* Shoot for around 75% usage */
+static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)];
+static const char sd_thread_name[] = "ata/sd";
+static struct mutex sd_mtx SHAREDBSS_ATTR;
+static struct event_queue sd_queue;
static inline void mci_delay(void) { int i = 0xffff; while(i--) ; }
@@ -106,11 +124,12 @@ static void mci_set_clock_divider(const int drive, int divider)
mci_delay();
}
-static int send_cmd(const int drive, struct mmc_command *cmd)
+static bool send_cmd(const int drive, const int cmd, const int arg,
+ const int flags, int *response)
{
int val, status;
- while(MMC_STATUS(drive) & MCI_CMD_ACTIVE); /* useless */
+ while(MMC_STATUS(drive) & MCI_CMD_ACTIVE);
if(MMC_COMMAND(drive) & MCI_COMMAND_ENABLE) /* clears existing command */
{
@@ -118,152 +137,226 @@ static int send_cmd(const int drive, struct mmc_command *cmd)
mci_delay();
}
- val = cmd->cmd | MCI_COMMAND_ENABLE;
- if(cmd->flags & MMC_RESP)
+ val = cmd | MCI_COMMAND_ENABLE;
+ if(flags & MMC_RESP)
{
val |= MCI_COMMAND_RESPONSE;
- if(cmd->flags & MMC_LONG_RESP)
+ if(flags & MMC_LONG_RESP)
val |= MCI_COMMAND_LONG_RESPONSE;
}
MMC_CLEAR(drive) = 0x7ff;
- MMC_ARGUMENT(drive) = (cmd->flags & MMC_ARG) ? cmd->arg : 0;
+ MMC_ARGUMENT(drive) = (flags & MMC_ARG) ? arg : 0;
MMC_COMMAND(drive) = val;
- while(MMC_STATUS(drive) & MCI_CMD_ACTIVE);
+ while(MMC_STATUS(drive) & MCI_CMD_ACTIVE); /* wait for cmd completion */
MMC_COMMAND(drive) = 0;
MMC_ARGUMENT(drive) = ~0;
- do
+ status = MMC_STATUS(drive);
+ MMC_CLEAR(drive) = 0x7ff;
+
+ if(flags & MMC_RESP)
{
- status = MMC_STATUS(drive);
- if(cmd->flags & MMC_RESP)
- {
- if(status & MCI_CMD_TIMEOUT)
+ if(status & MCI_CMD_TIMEOUT)
+ return false;
+ else if(status & (MCI_CMD_CRC_FAIL /* FIXME? */ | MCI_CMD_RESP_END))
+ { /* resp received */
+ if(flags & MMC_LONG_RESP)
{
- if(cmd->cmd == SEND_IF_COND)
- break; /* SDHC test can fail */
- panicf("Response timeout");
- }
- else if(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END))
- { /* resp received */
- cmd->resp[0] = MMC_RESP0(drive);
- if(cmd->flags & MMC_LONG_RESP)
- {
- cmd->resp[1] = MMC_RESP1(drive);
- cmd->resp[2] = MMC_RESP2(drive);
- cmd->resp[3] = MMC_RESP3(drive);
- }
- break;
+ /* store the response in little endian order for the words */
+ response[0] = MMC_RESP3(drive);
+ response[1] = MMC_RESP2(drive);
+ response[2] = MMC_RESP1(drive);
+ response[3] = MMC_RESP0(drive);
}
+ else
+ response[0] = MMC_RESP0(drive);
+ return true;
}
- else
- if(status & MCI_CMD_SENT)
- break;
-
- } while(1);
+ }
+ else if(status & MCI_CMD_SENT)
+ return true;
- MMC_CLEAR(drive) = 0x7ff;
- return status;
+ return false;
}
-static void sd_init_card(const int drive)
+static int sd_init_card(const int drive)
{
- struct mmc_command cmd_app, cmd_op_cond, cmd_idle, cmd_if_cond;
- int status;
- bool sdhc;
+ unsigned int c_size;
+ unsigned long c_mult;
-#ifdef DEBUG
- reset_screen();
- printf("now - powered up");
-#endif
+ int response;
+ int max_tries = 100; /* max acmd41 attemps */
+ bool sdhc;
- cmd_idle.cmd = GO_IDLE_STATE;
- cmd_idle.arg = 0;
- cmd_idle.flags = MMC_NO_FLAGS;
- if(send_cmd(drive, &cmd_idle) != MCI_CMD_SENT)
- panicf("goto idle failed!");
-#ifdef DEBUG
- else
- printf("now - idle");
-#endif
+ if(!send_cmd(drive, GO_IDLE_STATE, 0, MMC_NO_FLAGS, NULL))
+ return -1;
mci_delay();
- cmd_if_cond.cmd = SEND_IF_COND;
- cmd_if_cond.arg = (1 /* 2.7-3.6V */ << 8) | 0xAA /* check pattern */;
- cmd_if_cond.flags = MMC_RESP | MMC_ARG;
-
- cmd_app.cmd = APP_CMD;
- cmd_app.flags = MMC_RESP | MMC_ARG;
- cmd_app.arg = 0; /* 31:16 RCA (0) , 15:0 stuff bits */
-
- cmd_op_cond.cmd = SEND_OP_COND;
- cmd_op_cond.flags = MMC_RESP | MMC_ARG;
-
-#ifdef DEBUG
- printf("now - card powering up");
-#endif
-
sdhc = false;
- status = send_cmd(drive, &cmd_if_cond);
- if(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END))
- {
- if((cmd_if_cond.resp[0] & 0xFFF) == cmd_if_cond.arg)
+ if(send_cmd(drive, SEND_IF_COND, 0x1AA, MMC_RESP|MMC_ARG, &response))
+ if((response & 0xFFF) == 0x1AA)
sdhc = true;
-#ifdef DEBUG
- else
- printf("Bad resp: %x",cmd_if_cond.arg);
-#endif
- }
-#ifdef DEBUG
- else
- printf("cmd_if_cond stat: 0x%x",status);
-
- printf("%s Capacity",sdhc?"High":"Normal");
- mci_delay();
- mci_delay();
- mci_delay();
-#endif
-#ifdef DEBUG
- int loop = 0;
-#endif
do {
mci_delay();
- mci_delay();
-#ifdef DEBUG
- reset_screen();
- printf("Loop number #%d", ++loop);
-#endif
+
/* app_cmd */
- status = send_cmd(drive, &cmd_app);
- if( !(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END)) ||
- !(cmd_app.resp[0] & (1<<5)) )
+ if( !send_cmd(drive, APP_CMD, 0, MMC_RESP|MMC_ARG, &response) ||
+ !(response & (1<<5)) )
{
- panicf("app_cmd failed");
+ return -2;
}
- cmd_op_cond.arg = sdhc ? 0x40FF8000 : (8<<0x14); /* ocr */
- status = send_cmd(drive, &cmd_op_cond);
- if(!(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END)))
- panicf("cmd_op_cond failed");
+ /* acmd41 */
+ if(!send_cmd(drive, SD_APP_OP_COND, (sdhc ? 0x40FF8000 : (1<<23)),
+ MMC_RESP|MMC_ARG, &card_info[drive].ocr))
+ return -3;
-#ifdef DEBUG
- printf("OP COND: 0x%.8x", cmd_op_cond.resp[0]);
-#endif
- } while(!(cmd_op_cond.resp[0] & (1<<31))); /* until card is powered up */
+ } while(!(card_info[drive].ocr & (1<<31)) && max_tries--);
+
+ if(!max_tries)
+ return -4;
+
+ /* send CID */
+ if(!send_cmd(drive, ALL_SEND_CID, 0, MMC_RESP|MMC_LONG_RESP|MMC_ARG,
+ card_info[drive].cid))
+ return -5;
+
+ /* send RCA */
+ if(!send_cmd(drive, SEND_RELATIVE_ADDR, 0, MMC_RESP|MMC_ARG,
+ &card_info[drive].rca))
+ return -6;
-#ifdef DEBUG
- printf("now - card ready !");
+ /* send CSD */
+ if(!send_cmd(drive, SEND_CSD, card_info[drive].rca,
+ MMC_RESP|MMC_LONG_RESP|MMC_ARG, card_info[drive].csd))
+ return -7;
+
+ /* These calculations come from the Sandisk SD card product manual */
+ if( (card_info[drive].csd[3]>>30) == 0)
+ {
+ /* CSD version 1.0 */
+ c_size = ((card_info[drive].csd[2] & 0x3ff) << 2) + (card_info[drive].csd[1]>>30) + 1;
+ c_mult = 4 << ((card_info[drive].csd[1] >> 15) & 7);
+ card_info[drive].max_read_bl_len = 1 << ((card_info[drive].csd[2] >> 16) & 15);
+ card_info[drive].block_size = BLOCK_SIZE; /* Always use 512 byte blocks */
+ card_info[drive].numblocks = c_size * c_mult * (card_info[drive].max_read_bl_len/512);
+ card_info[drive].capacity = card_info[drive].numblocks * card_info[drive].block_size;
+ }
+#ifdef HAVE_MULTIVOLUME
+ else if( (card_info[drive].csd[3]>>30) == 1)
+ {
+ /* CSD version 2.0 */
+ c_size = ((card_info[drive].csd[2] & 0x3f) << 16) + (card_info[drive].csd[1]>>16) + 1;
+ card_info[drive].max_read_bl_len = 1 << ((card_info[drive].csd[2] >> 16) & 0xf);
+ card_info[drive].block_size = BLOCK_SIZE; /* Always use 512 byte blocks */
+ card_info[drive].numblocks = c_size << 10;
+ card_info[drive].capacity = card_info[drive].numblocks * card_info[drive].block_size;
+ }
#endif
+
+ if(!send_cmd(drive, SELECT_CARD, card_info[drive].rca, MMC_ARG, NULL))
+ return -9;
+
+ if(!send_cmd(drive, APP_CMD, card_info[drive].rca, MMC_ARG, NULL))
+ return -10;
+
+ if(!send_cmd(drive, SET_BUS_WIDTH, card_info[drive].rca | 2, MMC_ARG, NULL))
+ return -11;
+
+ if(!send_cmd(drive, SET_BLOCKLEN, card_info[drive].block_size, MMC_ARG,
+ NULL))
+ return -12;
+
+ card_info[drive].initialized = 1;
+
+ mci_set_clock_divider(drive, 1); /* full speed */
+
+ return 0;
}
+static void sd_thread(void) __attribute__((noreturn));
+static void sd_thread(void)
+{
+ struct queue_event ev;
+ bool idle_notified = false;
+
+ while (1)
+ {
+ queue_wait_w_tmo(&sd_queue, &ev, HZ);
+
+ switch ( ev.id )
+ {
+#ifdef HAVE_HOTSWAP
+ case SYS_HOTSWAP_INSERTED:
+ case SYS_HOTSWAP_EXTRACTED:
+ fat_lock(); /* lock-out FAT activity first -
+ prevent deadlocking via disk_mount that
+ would cause a reverse-order attempt with
+ another thread */
+ mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
+ into driver that bypass the fat cache */
+
+ /* We now have exclusive control of fat cache and ata */
+
+ disk_unmount(1); /* release "by force", ensure file
+ descriptors aren't leaked and any busy
+ ones are invalid if mounting */
+
+ /* Force card init for new card, re-init for re-inserted one or
+ * clear if the last attempt to init failed with an error. */
+ card_info[1].initialized = 0;
+
+ if (ev.id == SYS_HOTSWAP_INSERTED)
+ disk_mount(1);
+
+ queue_broadcast(SYS_FS_CHANGED, 0);
+
+ /* Access is now safe */
+ mutex_unlock(&sd_mtx);
+ fat_unlock();
+ break;
+#endif
+ case SYS_TIMEOUT:
+ if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
+ {
+ idle_notified = false;
+ }
+ else
+ {
+ /* never let a timer wrap confuse us */
+ next_yield = current_tick;
+
+ if (!idle_notified)
+ {
+ call_storage_idle_notifys(false);
+ idle_notified = true;
+ }
+ }
+ break;
+#if 0
+ case SYS_USB_CONNECTED:
+ usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ /* Wait until the USB cable is extracted again */
+ usb_wait_for_disconnect(&sd_queue);
+
+ break;
+ case SYS_USB_DISCONNECTED:
+ usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
+ break;
+#endif
+ }
+ }
+}
static void init_pl180_controller(const int drive)
{
- MMC_COMMAND(drive) = MMC_DATACTRL(drive) = 0;
+#ifdef BOOTLOADER
+ MMC_COMMAND(drive) = MMC_DATA_CTRL(drive) = 0;
MMC_CLEAR(drive) = 0x7ff;
MMC_MASK0(drive) = MMC_MASK1(drive) = 0; /* disable all interrupts */
@@ -281,10 +374,16 @@ static void init_pl180_controller(const int drive)
/* set MCLK divider */
mci_set_clock_divider(drive, 200);
+#else
+ /* controller already initialized by bootloader */
+ (void)drive;
+#endif /* BOOTLOADER */
}
int sd_init(void)
{
+ int ret;
+
CGU_IDE = (1<<7) /* AHB interface enable */ |
(1<<6) /* interface enable */ |
(2<<2) /* clock didiver = 2+1 */ |
@@ -299,23 +398,55 @@ int sd_init(void)
CCU_IO |= 4;
init_pl180_controller(NAND_AS3525);
- sd_init_card(NAND_AS3525);
+ ret = sd_init_card(NAND_AS3525);
+ if(ret < 0)
+ return ret;
#ifdef HAVE_MULTIVOLUME
init_pl180_controller(SD_AS3525);
- sd_init_card(SD_AS3525);
+ ret = sd_init_card(SD_AS3525);
+ if(ret < 0)
+ return ret;
#endif
+ queue_init(&sd_queue, true);
+ create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0,
+ sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
+
return 0;
}
-int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int count, void* buf)
+#ifdef STORAGE_GET_INFO
+void sd_get_info(IF_MV2(int drive,) struct storage_info *info)
{
- (void)start;
- (void)count;
- (void)buf;
- return 0; /* TODO */
+#ifndef HAVE_MULTIVOLUME
+ const int drive=0;
+#endif
+ info->sector_size=card_info[drive].block_size;
+ info->num_sectors=card_info[drive].numblocks;
+ info->vendor="Rockbox";
+ info->product = (drive == 0) ? "Internal Storage" : "SD Card Slot";
+ info->revision="0.00";
+}
+#endif
+
+#ifdef HAVE_HOTSWAP
+bool sd_removable(IF_MV_NONVOID(int drive))
+{
+#ifndef HAVE_MULTIVOLUME
+ const int drive=0;
+#endif
+ return (drive==1);
+}
+
+bool sd_present(IF_MV_NONVOID(int drive))
+{
+#ifndef HAVE_MULTIVOLUME
+ const int drive=0;
+#endif
+ return (card_info[drive].initialized && card_info[drive].numblocks > 0);
}
+#endif
int sd_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf)
{
@@ -324,3 +455,178 @@ int sd_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const vo
(void)buf;
return 0; /* TODO */
}
+
+static bool sd_poll_status(const int drive, unsigned int trigger, long timeout)
+{
+ long t = current_tick;
+
+ while ((MMC_STATUS(drive) & trigger) == 0)
+ {
+ long time = current_tick;
+
+ if (TIME_AFTER(time, next_yield))
+ {
+ long ty = current_tick;
+ yield();
+ timeout += current_tick - ty;
+ next_yield = ty + MIN_YIELD_PERIOD;
+ }
+
+ if (TIME_AFTER(time, t + timeout))
+ return false;
+ }
+
+ return true;
+}
+
+static int sd_wait_for_state(const int drive, unsigned int state)
+{
+ unsigned int response = 0;
+ unsigned int timeout = 0x80000;
+
+ long t = current_tick;
+
+ while (1)
+ {
+ long us;
+
+ if(!send_cmd(drive, SEND_STATUS, card_info[drive].rca,
+ MMC_RESP|MMC_ARG, &response))
+ return -1;
+
+ if (((response >> 9) & 0xf) == state)
+ return 0;
+
+ if(TIME_AFTER(current_tick, t + timeout))
+ return -1;
+
+ us = current_tick;
+ if (TIME_AFTER(us, next_yield))
+ {
+ yield();
+ timeout += current_tick - us;
+ next_yield = us + MIN_YIELD_PERIOD;
+ }
+ }
+}
+
+int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
+ void* inbuf)
+{
+#ifndef HAVE_MULTIVOLUME
+ const int drive = 0;
+#endif
+ int ret;
+ unsigned char *buf_end, *buf = inbuf;
+ int remaining = incount;
+ const unsigned long *fifo_base = MMC_FIFO(drive);
+
+ start += 20480; /* skip SanDisk OF */
+
+ /* TODO: Add DMA support. */
+
+ mutex_lock(&sd_mtx);
+
+#ifdef HAVE_MULTIVOLUME
+ if (drive != 0 && !card_detect_target())
+ {
+ /* no external sd-card inserted */
+ ret = -88;
+ goto sd_read_error;
+ }
+#endif
+
+ if (card_info[drive].initialized < 0)
+ {
+ ret = card_info[drive].initialized;
+ goto sd_read_error;
+ }
+
+ last_disk_activity = current_tick;
+
+ ret = sd_wait_for_state(drive, TRAN);
+ if (ret < 0)
+ goto sd_read_error;
+
+ while(remaining)
+ {
+ /* 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 */
+
+ if(card_info[drive].ocr & (1<<30) ) /* SDHC */
+ ret = send_cmd(drive, READ_MULTIPLE_BLOCK, start, MMC_ARG, NULL);
+ else
+ ret = send_cmd(drive, READ_MULTIPLE_BLOCK, start * BLOCK_SIZE,
+ MMC_ARG, NULL);
+
+ if (ret < 0)
+ goto sd_read_error;
+
+ /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */
+
+
+ MMC_DATA_TIMER(drive) = 0x1000000; /* FIXME: arbitrary */
+ MMC_DATA_LENGTH(drive) = transfer * card_info[drive].block_size;
+ MMC_DATA_CTRL(drive) = (1<<0) /* enable */ |
+ (1<<1) /* from card to controller */ |
+ (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 */
+ if (!sd_poll_status(drive, ((1<<15)), 100))
+ {
+ ret = -42;
+ goto sd_read_error;
+ }
+
+ 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","r8" /* clobbers */
+ );
+ }
+
+ remaining -= transfer;
+ start += transfer;
+ last_disk_activity = current_tick;
+
+ if(!send_cmd(drive, STOP_TRANSMISSION, 0, MMC_NO_FLAGS, NULL))
+ {
+ ret = -666;
+ goto sd_read_error;
+ }
+
+ ret = sd_wait_for_state(drive, TRAN);
+ if (ret < 0)
+ goto sd_read_error;
+
+ }
+ while (1)
+ {
+ mutex_unlock(&sd_mtx);
+
+ return ret;
+
+sd_read_error:
+ card_info[drive].initialized = 0;
+ }
+}
+
+void sd_sleep(void)
+{
+}
+
+void sd_spin(void)
+{
+}
+
+void sd_spindown(int seconds)
+{
+ (void)seconds;
+}
diff --git a/firmware/target/arm/as3525/kernel-as3525.c b/firmware/target/arm/as3525/kernel-as3525.c
index 73031b9eb5..c534d5e130 100644
--- a/firmware/target/arm/as3525/kernel-as3525.c
+++ b/firmware/target/arm/as3525/kernel-as3525.c
@@ -32,12 +32,9 @@ void INT_TIMER2(void)
void tick_start(unsigned int interval_in_ms)
{
-#ifdef BOOTLOADER
- (void) interval_in_ms;
-#else
int phi = 0; /* prescaler bits */
int prescale = 1;
- int cycles = 64000 * interval_in_ms; /* pclk is clocked at 64MHz */
+ int cycles = 1000 * interval_in_ms; /* pclk is clocked at 64MHz */
while(cycles > 0x10000)
{
@@ -57,5 +54,4 @@ void tick_start(unsigned int interval_in_ms)
/* /!\ bit 4 (reserved) must not be modified
* periodic mode, interrupt enabled, 16 bits counter */
TIMER2_CONTROL = (TIMER2_CONTROL & (1<<4)) | 0xe0 | (phi<<2);
-#endif
}
diff --git a/firmware/target/arm/as3525/sansa-clip/system-target.h b/firmware/target/arm/as3525/sansa-clip/system-target.h
index b712d1c124..dc9d77f3dc 100644
--- a/firmware/target/arm/as3525/sansa-clip/system-target.h
+++ b/firmware/target/arm/as3525/sansa-clip/system-target.h
@@ -23,6 +23,8 @@
#include "system-arm.h"
-#define CPUFREQ_MAX 250000000
+#define CPUFREQ_MAX 250000000
+#define CPUFREQ_DEFAULT 250000000
+#define CPUFREQ_NORMAL 250000000
#endif /* SYSTEM_TARGET_H */
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index 544371e5a4..240cb63b7c 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -23,6 +23,7 @@
#include "kernel.h"
#include "system.h"
#include "panic.h"
+#include "as3525-codec.h"
#define default_interrupt(name) \
extern __attribute__((weak,alias("UIRQ"))) void name (void)
@@ -123,6 +124,7 @@ void fiq_handler(void)
);
}
+#ifdef BOOTLOADER
static void sdram_delay(void)
{
int delay = 1024; /* arbitrary */
@@ -192,9 +194,11 @@ static void sdram_init(void)
MPMC_DYNAMIC_CONFIG_0 |= (1<<19); /* buffer enable */
}
+#endif
void system_init(void)
{
+#ifdef BOOTLOADER
#if 0 /* the GPIO clock is already enabled by the dualboot function */
CGU_PERI |= CGU_GPIO_CLOCK_ENABLE;
#endif
@@ -235,6 +239,7 @@ void system_init(void)
/* enable VIC */
CGU_PERI |= CGU_VIC_CLOCK_ENABLE;
VIC_INT_SELECT = 0; /* only IRQ, no FIQ */
+#endif
}
void system_reboot(void)
@@ -246,3 +251,15 @@ int system_memory_guard(int newmode)
(void)newmode;
return 0;
}
+
+void power_off(void)
+{
+ int system;
+ system = as3525_codec_read(0x20);
+ system &= ~1; /* clear bit 0 of system register */
+ as3525_codec_write(0x20, system);
+
+ /* TODO : turn off peripherals properly ? */
+
+ while(1);
+}