From aef27e1f0c69516bbcf13d7986e204502d708ec4 Mon Sep 17 00:00:00 2001 From: Rafaël Carré Date: Sun, 9 Nov 2008 06:17:21 +0000 Subject: Sansav2 Bootloader Adds read-only SD driver, largely copied from ata-sd-pp.c Only tested on the embedded SD, on the Clip First steps to build a Normal firmware git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19045 a1c6a512-1295-4272-9138-f99709370657 --- firmware/export/config-clip.h | 12 +- firmware/export/usb.h | 1 + firmware/target/arm/as3525/app.lds | 126 +++++ firmware/target/arm/as3525/ata_sd_as3525.c | 590 ++++++++++++++++----- firmware/target/arm/as3525/kernel-as3525.c | 6 +- .../target/arm/as3525/sansa-clip/system-target.h | 4 +- firmware/target/arm/as3525/system-as3525.c | 17 + 7 files changed, 605 insertions(+), 151 deletions(-) create mode 100644 firmware/target/arm/as3525/app.lds (limited to 'firmware') diff --git a/firmware/export/config-clip.h b/firmware/export/config-clip.h index cfeaea770c..9bc4a14bf5 100644 --- a/firmware/export/config-clip.h +++ b/firmware/export/config-clip.h @@ -6,7 +6,7 @@ /* For Rolo and boot loader */ #define MODEL_NUMBER 40 #define MODEL_NAME "Sandisk Sansa Clip" -#define FIRMWARE_OFFSET_FILE_DATA 0 +#define FIRMWARE_OFFSET_FILE_DATA 8 #define FIRMWARE_OFFSET_FILE_CRC 0 #define HW_SAMPR_CAPS (SAMPR_CAP_44) @@ -84,7 +84,11 @@ #define CODEC_SIZE 0x100000 /* The number of bytes reserved for loadable plugins */ +#if 0 /* The plugin buffer doesn't fit in the 2MB memory */ #define PLUGIN_BUFFER_SIZE 0x80000 +#else +#define PLUGIN_BUFFER_SIZE 0 +#endif #define AB_REPEAT_ENABLE 1 @@ -137,8 +141,8 @@ #define CONFIG_LCD LCD_SSD1303 #ifndef BOOTLOADER -#define HAVE_MULTIVOLUME -#define HAVE_HOTSWAP + +#if 0 /* disabled since there is no USB driver */ /* USB On-the-go */ #define CONFIG_USBOTG USBOTG_ARC @@ -149,6 +153,8 @@ #define USB_PRODUCT_ID 0x7433 #endif /* BOOTLOADER */ +#endif + /* Virtual LED (icon) */ #define CONFIG_LED LED_VIRTUAL diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 08ae27999a..4275fa9c6a 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -63,6 +63,7 @@ enum { #define USBPOWER_BTN_IGNORE BUTTON_POWER #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \ (CONFIG_KEYPAD == SANSA_C200_PAD) || \ + (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \ (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) #define USBPOWER_BUTTON BUTTON_SELECT #define USBPOWER_BTN_IGNORE BUTTON_POWER 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 #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); +} -- cgit v1.2.3