From 1167e3c72f5d0d581b81fd2cb8f2580a1524ca5a Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 30 Jun 2007 02:08:27 +0000 Subject: [PATCH] Accept FS#7134 - Sansa: external sd card support by Antonius Hellmann with some tweaks. All testers have given the green light. (Now for the RED ?? ;). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13741 a1c6a512-1295-4272-9138-f99709370657 --- apps/debug_menu.c | 46 +- apps/main.c | 3 +- apps/misc.c | 3 +- apps/screens.c | 4 +- bootloader/main-pp.c | 20 +- firmware/SOURCES | 3 + firmware/common/dir.c | 3 + firmware/common/disk.c | 16 +- firmware/drivers/ata_mmc.c | 48 +- firmware/export/ata_mmc.h | 24 +- firmware/export/config-e200.h | 5 + firmware/export/config.h | 2 +- firmware/export/hotswap.h | 59 + firmware/export/kernel.h | 4 +- firmware/export/pp5024.h | 2 +- firmware/hotswap.c | 58 + firmware/include/dir.h | 4 + .../target/arm/sandisk/sansa-e200/ata-e200.c | 1039 ++++++++++------- .../arm/sandisk/sansa-e200/ata-target.h | 21 - .../arm/sandisk/sansa-e200/hotswap-target.h | 45 + firmware/target/arm/system-pp502x.c | 13 +- firmware/usb.c | 4 +- 22 files changed, 886 insertions(+), 540 deletions(-) create mode 100644 firmware/export/hotswap.h create mode 100644 firmware/hotswap.c create mode 100644 firmware/target/arm/sandisk/sansa-e200/hotswap-target.h diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 0b39430d56..fe7d5b584b 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -59,7 +59,7 @@ #include "fat.h" #include "mas.h" #include "eeprom_24cxx.h" -#ifdef HAVE_MMC +#if defined(HAVE_MMC) || defined(HAVE_HOTSWAP) #include "ata_mmc.h" #endif #if CONFIG_TUNER @@ -1524,8 +1524,8 @@ static bool view_battery(void) #endif #ifndef SIMULATOR -#ifdef HAVE_MMC -static bool dbg_mmc_info(void) +#if defined(HAVE_MMC) || defined(HAVE_HOTSWAP) +static bool dbg_card_info(void) { bool done = false; int currval = 0; @@ -1548,7 +1548,7 @@ static bool dbg_mmc_info(void) while (!done) { - card = mmc_card_info(currval / 2); + card = card_get_info(currval / 2); line = 0; lcd_clear_display(); @@ -1564,29 +1564,29 @@ static bool dbg_mmc_info(void) strncpy(card_name, ((unsigned char*)card->cid) + 3, 6); snprintf(pbuf, sizeof(pbuf), "%s Rev %d.%d", card_name, - (int) mmc_extract_bits(card->cid, 72, 4), - (int) mmc_extract_bits(card->cid, 76, 4)); + (int) card_extract_bits(card->cid, 72, 4), + (int) card_extract_bits(card->cid, 76, 4)); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "Prod: %d/%d", - (int) mmc_extract_bits(card->cid, 112, 4), - (int) mmc_extract_bits(card->cid, 116, 4) + 1997); + (int) card_extract_bits(card->cid, 112, 4), + (int) card_extract_bits(card->cid, 116, 4) + 1997); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "Ser#: 0x%08lx", - mmc_extract_bits(card->cid, 80, 32)); + card_extract_bits(card->cid, 80, 32)); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "M=%02x, O=%04x", - (int) mmc_extract_bits(card->cid, 0, 8), - (int) mmc_extract_bits(card->cid, 8, 16)); + (int) card_extract_bits(card->cid, 0, 8), + (int) card_extract_bits(card->cid, 8, 16)); lcd_puts(0, line++, pbuf); - temp = mmc_extract_bits(card->csd, 2, 4); + temp = card_extract_bits(card->csd, 2, 4); snprintf(pbuf, sizeof(pbuf), "MMC v%s", temp < 5 ? spec_vers[temp] : "?.?"); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "Blocks: 0x%06lx", card->numblocks); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "Blksz.: %d P:%c%c", card->blocksize, - mmc_extract_bits(card->csd, 48, 1) ? 'R' : '-', - mmc_extract_bits(card->csd, 106, 1) ? 'W' : '-'); + card_extract_bits(card->csd, 48, 1) ? 'R' : '-', + card_extract_bits(card->csd, 106, 1) ? 'W' : '-'); lcd_puts(0, line++, pbuf); } else /* Technical details */ @@ -1606,12 +1606,12 @@ static bool dbg_mmc_info(void) snprintf(pbuf, sizeof(pbuf), "R2W: *%d", card->r2w_factor); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "IRmax: %d..%d mA", - i_vmin[mmc_extract_bits(card->csd, 66, 3)], - i_vmax[mmc_extract_bits(card->csd, 69, 3)]); + i_vmin[card_extract_bits(card->csd, 66, 3)], + i_vmax[card_extract_bits(card->csd, 69, 3)]); lcd_puts(0, line++, pbuf); snprintf(pbuf, sizeof(pbuf), "IWmax: %d..%d mA", - i_vmin[mmc_extract_bits(card->csd, 72, 3)], - i_vmax[mmc_extract_bits(card->csd, 75, 3)]); + i_vmin[card_extract_bits(card->csd, 72, 3)], + i_vmax[card_extract_bits(card->csd, 75, 3)]); lcd_puts(0, line++, pbuf); } } @@ -1642,7 +1642,7 @@ static bool dbg_mmc_info(void) action_signalscreenchange(); return false; } -#else /* !HAVE_MMC */ +#else /* !defined(HAVE_MMC) && !defined(HAVE_HOTSWAP) */ static bool dbg_disk_info(void) { char buf[128]; @@ -1799,7 +1799,7 @@ static bool dbg_disk_info(void) action_signalscreenchange(); return false; } -#endif /* !HAVE_MMC */ +#endif /* !defined(HAVE_MMC) && !defined(HAVE_HOTSWAP) */ #endif /* !SIMULATOR */ #ifdef HAVE_DIRCACHE @@ -2278,8 +2278,10 @@ static const struct the_menu_item menuitems[] = { { "View partitions", dbg_partitions }, #endif #ifndef SIMULATOR -#ifdef HAVE_MMC - { "View MMC info", dbg_mmc_info }, +#if defined(HAVE_MMC) + { "View MMC info", dbg_card_info }, +#elif defined(HAVE_HOTSWAP) + { "View microSD info", dbg_card_info }, #else { "View disk info", dbg_disk_info }, #endif diff --git a/apps/main.c b/apps/main.c index 3dfab06280..2dd90ef5fe 100644 --- a/apps/main.c +++ b/apps/main.c @@ -430,7 +430,8 @@ static void init(void) /* enter USB mode early, before trying to mount */ if (button_get_w_tmo(HZ/10) == SYS_USB_CONNECTED) #ifdef HAVE_MMC - if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED)) + if (!mmc_touched() || + (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED)) #endif { usb_screen(); diff --git a/apps/misc.c b/apps/misc.c index 88ec73e2d3..daa96b006a 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -828,7 +828,8 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame if (callback != NULL) callback(parameter); #ifdef HAVE_MMC - if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED)) + if (!mmc_touched() || + (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED)) #endif { scrobbler_flush_cache(); diff --git a/apps/screens.c b/apps/screens.c index d8e3121121..aebb7bd469 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -163,8 +163,8 @@ int mmc_remove_request(void) queue_wait_w_tmo(&button_queue, &ev, HZ/2); switch (ev.id) { - case SYS_MMC_EXTRACTED: - return SYS_MMC_EXTRACTED; + case SYS_HOTSWAP_EXTRACTED: + return SYS_HOTSWAP_EXTRACTED; case SYS_USB_DISCONNECTED: return SYS_USB_DISCONNECTED; diff --git a/bootloader/main-pp.c b/bootloader/main-pp.c index 49d7f0a2f7..dd8b7984ca 100644 --- a/bootloader/main-pp.c +++ b/bootloader/main-pp.c @@ -352,7 +352,7 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, unsigned long sum; /* Read header to find out how long the mi4 file is. */ - ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET, + ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET, PPMI_SECTORS, &ppmi_header); /* The first four characters at 0x80000 (sector 1024) should be PPMI*/ @@ -362,7 +362,7 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, printf("BL mi4 size: %x", ppmi_header.length); /* Read mi4 header of the OF */ - ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + (ppmi_header.length/512), MI4_HEADER_SECTORS, &mi4header); /* We don't support encrypted mi4 files yet */ @@ -385,7 +385,7 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, printf("Binary type: %.4s", mi4header.type); /* Load firmware */ - ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + (ppmi_header.length/512) + MI4_HEADER_SECTORS, (mi4header.mi4size-MI4_HEADER_SIZE)/512, buf); @@ -404,7 +404,7 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, unsigned int i; /* check which known version we have */ /* These are taken from the PPPS section, 0x00780240 */ - ata_read_sectors(pinfo->start + 0x3C01, 1, block); + ata_read_sectors(IF_MV2(0,) pinfo->start + 0x3C01, 1, block); for (i=0; i 32) /* crossing longword boundary */ - result |= p[long_index+1] >> (32 - bit_index); - - result >>= 32 - size; - - return result; -} static int initialize_card(int card_no) { @@ -470,9 +450,9 @@ static int initialize_card(int card_no) return rc * 10 - 6; /* check block sizes */ - card->block_exp = mmc_extract_bits(card->csd, 44, 4); + card->block_exp = card_extract_bits(card->csd, 44, 4); card->blocksize = 1 << card->block_exp; - if ((mmc_extract_bits(card->csd, 102, 4) != card->block_exp) + if ((card_extract_bits(card->csd, 102, 4) != card->block_exp) || card->blocksize > MAX_BLOCK_SIZE) { return -7; @@ -486,16 +466,16 @@ static int initialize_card(int card_no) } /* max transmission speed, clock divider */ - temp = mmc_extract_bits(card->csd, 29, 3); + temp = card_extract_bits(card->csd, 29, 3); temp = (temp > 3) ? 3 : temp; - card->speed = mantissa[mmc_extract_bits(card->csd, 25, 4)] + card->speed = mantissa[card_extract_bits(card->csd, 25, 4)] * exponent[temp + 4]; card->bitrate_register = (FREQ/4-1) / card->speed; /* NSAC, TSAC, read timeout */ - card->nsac = 100 * mmc_extract_bits(card->csd, 16, 8); - card->tsac = mantissa[mmc_extract_bits(card->csd, 9, 4)]; - temp = mmc_extract_bits(card->csd, 13, 3); + card->nsac = 100 * card_extract_bits(card->csd, 16, 8); + card->tsac = mantissa[card_extract_bits(card->csd, 9, 4)]; + temp = card_extract_bits(card->csd, 13, 3); card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1) * card->tsac / exponent[9 - temp] + (10 * card->nsac)); @@ -503,7 +483,7 @@ static int initialize_card(int card_no) card->tsac = card->tsac * exponent[temp] / 10; /* r2w_factor, write timeout */ - card->r2w_factor = 1 << mmc_extract_bits(card->csd, 99, 3); + card->r2w_factor = 1 << card_extract_bits(card->csd, 99, 3); if (card->r2w_factor > 32) /* dirty MMC spec violation */ { card->read_timeout *= 4; /* add safety factor */ @@ -513,8 +493,8 @@ static int initialize_card(int card_no) card->write_timeout = card->read_timeout * card->r2w_factor; /* card size */ - card->numblocks = (mmc_extract_bits(card->csd, 54, 12) + 1) - * (1 << (mmc_extract_bits(card->csd, 78, 3) + 2)); + card->numblocks = (card_extract_bits(card->csd, 54, 12) + 1) + * (1 << (card_extract_bits(card->csd, 78, 3) + 2)); card->size = card->numblocks * card->blocksize; /* switch to full speed */ @@ -993,12 +973,12 @@ static void mmc_thread(void) break; #ifdef HAVE_HOTSWAP - case SYS_MMC_INSERTED: + case SYS_HOTSWAP_INSERTED: disk_mount(1); /* mount MMC */ queue_broadcast(SYS_FS_CHANGED, 0); break; - case SYS_MMC_EXTRACTED: + case SYS_HOTSWAP_EXTRACTED: disk_unmount(1); /* release "by force" */ queue_broadcast(SYS_FS_CHANGED, 0); break; @@ -1097,11 +1077,11 @@ static void mmc_tick(void) { if (current_status) { - queue_broadcast(SYS_MMC_INSERTED, 0); + queue_broadcast(SYS_HOTSWAP_INSERTED, 0); } else { - queue_broadcast(SYS_MMC_EXTRACTED, 0); + queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); mmc_status = MMC_UNTOUCHED; card_info[1].initialized = false; } diff --git a/firmware/export/ata_mmc.h b/firmware/export/ata_mmc.h index a4e9f71f09..2361c439b5 100644 --- a/firmware/export/ata_mmc.h +++ b/firmware/export/ata_mmc.h @@ -18,34 +18,14 @@ ****************************************************************************/ #ifndef __ATA_MMC_H__ #define __ATA_MMC_H__ - -typedef struct -{ - bool initialized; - unsigned char bitrate_register; - unsigned long read_timeout; /* n * 8 clock cycles */ - unsigned long write_timeout; /* n * 8 clock cycles */ - - unsigned long ocr; /* OCR register */ - unsigned long csd[4]; /* CSD register, 16 bytes */ - unsigned long cid[4]; /* CID register, 16 bytes */ - unsigned long speed; /* bit/s */ - unsigned int nsac; /* clock cycles */ - unsigned long tsac; /* n * 0.1 ns */ - unsigned int r2w_factor; - unsigned long size; /* size in bytes */ - unsigned long numblocks; /* size in flash blocks */ - unsigned int blocksize; /* block size in bytes */ - unsigned int block_exp; /* block size exponent */ -} tCardInfo; +#include "hotswap.h" void mmc_enable_int_flash_clock(bool on); bool mmc_detect(void); -unsigned long mmc_extract_bits(const unsigned long *p, unsigned int start, - unsigned int size); tCardInfo *mmc_card_info(int card_no); bool mmc_touched(void); bool mmc_usb_active(int delayticks); + #ifdef HAVE_HOTSWAP void mmc_enable_monitoring(bool on); #endif diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h index 71b1270749..f9708697dc 100644 --- a/firmware/export/config-e200.h +++ b/firmware/export/config-e200.h @@ -33,6 +33,11 @@ /* define this if you have LCD enable function */ #define HAVE_LCD_ENABLE +#ifndef SIMULATOR +#define HAVE_HOTSWAP +#define HAVE_MULTIVOLUME +#endif + #define HAVE_BACKLIGHT_BRIGHTNESS /* Main LCD backlight brightness range and defaults */ #define MIN_BRIGHTNESS_SETTING 1 diff --git a/firmware/export/config.h b/firmware/export/config.h index 4652359e62..ffcbf688ea 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -247,7 +247,7 @@ /* Enable the directory cache and tagcache in RAM if we have * plenty of RAM. Both features can be enabled independently. */ #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8) && \ - !defined(BOOTLOADER) + !defined(BOOTLOADER) && !defined(SANSA_E200) #define HAVE_DIRCACHE #ifdef HAVE_TAGCACHE #define HAVE_TC_RAMCACHE diff --git a/firmware/export/hotswap.h b/firmware/export/hotswap.h new file mode 100644 index 0000000000..c6a657d731 --- /dev/null +++ b/firmware/export/hotswap.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2004 by Jens Arnold + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __HOTSWAP_H__ +#define __HOTSWAP_H__ + +typedef struct +{ + bool initialized; + unsigned char bitrate_register; + unsigned long read_timeout; /* n * 8 clock cycles */ + unsigned long write_timeout; /* n * 8 clock cycles */ + + unsigned long ocr; /* OCR register */ + unsigned long csd[4]; /* CSD register, 16 bytes */ + unsigned long cid[4]; /* CID register, 16 bytes */ + unsigned long speed; /* bit/s */ + unsigned int nsac; /* clock cycles */ + unsigned long tsac; /* n * 0.1 ns */ + unsigned int r2w_factor; + unsigned long size; /* size in bytes */ + unsigned long numblocks; /* size in flash blocks */ + unsigned int blocksize; /* block size in bytes */ + unsigned int block_exp; /* block size exponent */ +} tCardInfo; + +#ifdef TARGET_TREE +bool card_detect(void); +tCardInfo *card_get_info(int card_no); +#else /* HAVE_MMC */ +#include "ata_mmc.h" +#define card_detect mmc_detect +#define card_get_info mmc_card_info +#define card_touched mmc_touched +#define card_enable_monitoring mmc_enable_monitoring +#endif + +/* helper function to extract n (<=32) bits from an arbitrary position. + counting from MSB to LSB */ +unsigned long card_extract_bits( + const unsigned long *p, /* the start of the bitfield array */ + unsigned int start, /* bit no. to start reading */ + unsigned int size); /* how many bits to read */ +#endif diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h index 9e3a8d25df..d5898a97f1 100644 --- a/firmware/export/kernel.h +++ b/firmware/export/kernel.h @@ -41,8 +41,8 @@ #define SYS_USB_DISCONNECTED ((SYS_EVENT | ((long)3 << 27))) #define SYS_USB_DISCONNECTED_ACK ((SYS_EVENT | ((long)4 << 27))) #define SYS_TIMEOUT ((SYS_EVENT | ((long)5 << 27))) -#define SYS_MMC_INSERTED ((SYS_EVENT | ((long)6 << 27))) -#define SYS_MMC_EXTRACTED ((SYS_EVENT | ((long)7 << 27))) +#define SYS_HOTSWAP_INSERTED ((SYS_EVENT | ((long)6 << 27))) +#define SYS_HOTSWAP_EXTRACTED ((SYS_EVENT | ((long)7 << 27))) #define SYS_POWEROFF ((SYS_EVENT | ((long)8 << 27))) #define SYS_FS_CHANGED ((SYS_EVENT | ((long)9 << 27))) #define SYS_CHARGER_CONNECTED ((SYS_EVENT | ((long)10 << 27))) diff --git a/firmware/export/pp5024.h b/firmware/export/pp5024.h index e7758b9ce5..5e2de17741 100644 --- a/firmware/export/pp5024.h +++ b/firmware/export/pp5024.h @@ -24,7 +24,7 @@ #include "pp5020.h" #undef GPIO_IRQ -/* Ports A, ?? */ +/* Ports A, B, ?? */ #define GPIO0_IRQ (32+0) /* Ports F, H, ?? */ #define GPIO1_IRQ (32+1) diff --git a/firmware/hotswap.c b/firmware/hotswap.c new file mode 100644 index 0000000000..5620edf10b --- /dev/null +++ b/firmware/hotswap.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2004 by Jens Arnold + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include "config.h" +#ifdef TARGET_TREE +#include "hotswap-target.h" +#else +#include "ata_mmc.h" +#endif + +/* helper function to extract n (<=32) bits from an arbitrary position. + counting from MSB to LSB */ +unsigned long card_extract_bits( + const unsigned long *p, /* the start of the bitfield array */ + unsigned int start, /* bit no. to start reading */ + unsigned int size) /* how many bits to read */ +{ + unsigned int long_index = start / 32; + unsigned int bit_index = start % 32; + unsigned long result; + + result = p[long_index] << bit_index; + + if (bit_index + size > 32) /* crossing longword boundary */ + result |= p[long_index+1] >> (32 - bit_index); + + result >>= 32 - size; + + return result; +} + +#ifdef TARGET_TREE +bool card_detect(void) +{ + return card_detect_target(); +} + +tCardInfo *card_get_info(int card_no) +{ + return card_get_info_target(card_no); +} +#endif diff --git a/firmware/include/dir.h b/firmware/include/dir.h index c354082bec..020b24a502 100644 --- a/firmware/include/dir.h +++ b/firmware/include/dir.h @@ -71,6 +71,10 @@ typedef struct { #endif } DIR; +#ifdef HAVE_HOTSWAP +char *get_volume_name(int volume); +#endif + #ifndef DIRFUNCTIONS_DEFINED extern DIR* opendir(const char* name); diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c index 8eccdfb729..fea6773ac9 100644 --- a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c +++ b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c @@ -17,14 +17,16 @@ * ****************************************************************************/ #include "ata.h" +#include "hotswap-target.h" #include "ata-target.h" #include "ata_idle_notify.h" #include "system.h" #include #include "thread.h" +#include "led.h" +#include "disk.h" #include "pp5024.h" - -#define NOINLINE_ATTR __attribute__((noinline)) /* don't inline the loops */ +#include "panic.h" #define BLOCK_SIZE (512) #define SECTOR_SIZE (512) @@ -48,6 +50,7 @@ #define DATA_DONE (1 << 12) #define CMD_DONE (1 << 13) #define ERROR_BITS (0x3f) +#define READY_FOR_DATA (1 << 8) #define FIFO_FULL (1 << 7) #define FIFO_EMPTY (1 << 6) @@ -65,370 +68,481 @@ #define FIFO_SIZE 16 /* FIFO is 16 words deep */ /* SD Commands */ -#define GO_IDLE_STATE 0 -#define ALL_SEND_CID 2 -#define SEND_RELATIVE_ADDR 3 -#define SET_DSR 4 -#define SWITCH_FUNC 6 -#define SELECT_CARD 7 -#define DESELECT_CARD 7 -#define SEND_CSD 9 -#define SEND_CID 10 -#define STOP_TRANSMISSION 12 -#define SEND_STATUS 13 -#define GO_INACTIVE_STATE 15 -#define SET_BLOCKLEN 16 -#define READ_SINGLE_BLOCK 17 -#define READ_MULTIPLE_BLOCK 18 -#define WRITE_BLOCK 24 -#define WRITE_MULTIPLE_BLOCK 25 -#define ERASE_WR_BLK_START 32 -#define ERASE_WR_BLK_END 33 -#define ERASE 38 +#define GO_IDLE_STATE 0 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SET_DSR 4 +#define SWITCH_FUNC 6 +#define SELECT_CARD 7 +#define DESELECT_CARD 7 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define GO_INACTIVE_STATE 15 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLE_BLOCK 18 +#define SEND_NUM_WR_BLOCKS 22 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLE_BLOCK 25 +#define ERASE_WR_BLK_START 32 +#define ERASE_WR_BLK_END 33 +#define ERASE 38 +#define APP_CMD 55 + +#define EC_POWER_UP 1 /* error code */ +#define EC_READ_TIMEOUT 2 /* error code */ +#define EC_WRITE_TIMEOUT 3 /* error code */ +#define EC_TRAN_SEL_BANK 4 /* error code */ +#define EC_TRAN_READ_ENTRY 5 /* error code */ +#define EC_TRAN_READ_EXIT 6 /* error code */ +#define EC_TRAN_WRITE_ENTRY 7 /* error code */ +#define EC_TRAN_WRITE_EXIT 8 /* error code */ +#define DO_PANIC 32 /* marker */ +#define NO_PANIC 0 /* marker */ +#define EC_COMMAND 10 /* error code */ +#define EC_FIFO_SEL_BANK_EMPTY 11 /* error code */ +#define EC_FIFO_SEL_BANK_DONE 12 /* error code */ +#define EC_FIFO_ENA_BANK_EMPTY 13 /* error code */ +#define EC_FIFO_READ_FULL 14 /* error code */ +#define EC_FIFO_WR_EMPTY 15 /* error code */ +#define EC_FIFO_WR_DONE 16 /* error code */ /* Application Specific commands */ #define SET_BUS_WIDTH 6 #define SD_APP_OP_COND 41 -#define READ_TIMEOUT 5*HZ -#define WRITE_TIMEOUT 0.5*HZ - -static unsigned short identify_info[SECTOR_SIZE]; +/* for compatibility */ int ata_spinup_time = 0; -long last_disk_activity = -1; -static bool initialized = false; -static unsigned char current_bank = 0; /* The bank that we are working with */ +long last_disk_activity = -1; + +static bool initialized = false; +static int sd1_status = 0x00; /* 0x00:inserted, 0x80:not inserted */ static tSDCardInfo card_info[2]; +static tSDCardInfo *currcard; /* current active card */ -/* For multi volume support */ -static int current_card = 0; - -static struct mutex sd_mtx; - -static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x800)/sizeof(long)]; - -static const char sd_thread_name[] = "sd"; +/* 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; static struct event_queue sd_queue; +/* Posted when card plugged status has changed */ +#define SD_HOTSWAP 1 /* Private Functions */ -bool sd_send_command(unsigned int cmd, unsigned long arg1, unsigned int arg2) +static unsigned int check_time[10]; + +static inline void sd_check_timeout(unsigned int timeout, int id) { - bool result = false; - do + if (USEC_TIMER > check_time[id] + timeout) + panicf("Error SDCard: %d", id); +} + +static inline bool sd_poll_status(unsigned int trigger, unsigned int timeout, + int id) +{ + unsigned int t = USEC_TIMER; + + while ((STATUS_REG & trigger) == 0) + { + if (USEC_TIMER > t + timeout) + { + if(id & DO_PANIC) + panicf("Error SDCard: %d", id & 31); + + return false; + } + } + + return true; +} + +static bool sd_command(unsigned int cmd, unsigned long arg1, + unsigned int *response, unsigned int type) +{ + int i, words; /* Number of 16 bit words to read from RESPONSE_REG */ + unsigned int data[9]; + + while (1) { CMD_REG0 = cmd; CMD_REG1 = (unsigned int)((arg1 & 0xffff0000) >> 16); CMD_REG2 = (unsigned int)((arg1 & 0xffff)); - UNKNOWN = arg2; - while ((STATUS_REG & CMD_DONE) == 0) - { - /* Busy wait */ - } - if ((STATUS_REG & ERROR_BITS) == 0) - { - result = true; - } - } while ((STATUS_REG & ERROR_BITS) != 0); - return result; -} + UNKNOWN = type; -void sd_read_response(unsigned int *response, int type) -{ - int i; - int words; /* Number of 16 bit words to read from RESPONSE_REG */ - unsigned int response_from_card[9]; - if(type == 2) - { - words = 9; /* R2 types are 8.5 16-bit words long */ - } else { - words = 3; + sd_poll_status(CMD_DONE, 100000, EC_COMMAND | DO_PANIC); + + if ((STATUS_REG & ERROR_BITS) == 0) + break; + + priority_yield(); } + if (cmd == GO_IDLE_STATE) return true; /* no response here */ + + words = (type == 2) ? 9 : 3; + for (i = 0; i < words; i++) /* RESPONSE_REG is read MSB first */ { - response_from_card[i] = RESPONSE_REG; /* Read most significant 16-bit word */ + data[i] = RESPONSE_REG; /* Read most significant 16-bit word */ } - switch (type) + if (type == 2) { - case 1: - /* Response type 1 has the following structure: - Start bit - Transmission bit - Command index (6 bits) - Card Status (32 bits) - CRC7 (7 bits) - Stop bit - */ - /* TODO: Sanity checks */ - response[0] = ((response_from_card[0] & 0xff) << 24) - + (response_from_card[1] << 8) - + ((response_from_card[2] & 0xff00) >> 8); - break; - case 2: - /* Response type 2 has the following structure: - Start bit - Transmission bit - Reserved (6 bits) - CSD/CID register (127 bits) - Stop bit - */ - response[3] = ((response_from_card[0]&0xff)<<24) + - (response_from_card[1]<<8) + - ((response_from_card[2]&0xff00)>>8); - response[2] = ((response_from_card[2]&0xff)<<24) + - (response_from_card[3]<<8) + - ((response_from_card[4]&0xff00)>>8); - response[1] = ((response_from_card[4]&0xff)<<24) + - (response_from_card[5]<<8) + - ((response_from_card[6]&0xff00)>>8); - response[0] = ((response_from_card[6]&0xff)<<24) + - (response_from_card[7]<<8) + - ((response_from_card[8]&0xff00)>>8); - break; - case 3: - /* Response type 3 has the following structure: - Start bit - Transmission bit - Reserved (6 bits) - OCR register (32 bits) - Reserved (7 bits) - Stop bit - */ - response[0] = ((response_from_card[0] & 0xff) << 24) - + (response_from_card[1] << 8) - + ((response_from_card[2] & 0xff00) >> 8); - /* Types 4-6 not supported yet */ + /* Response type 2 has the following structure: + * [135:135] Start Bit - '0' + * [134:134] Transmission bit - '0' + * [133:128] Reserved - '111111' + * [127:001] CID or CSD register including internal CRC7 + * [000:000] End Bit - '1' + */ + response[3] = (data[0]<<24) + (data[1]<<8) + ((data[2]&0xff00)>>8); + response[2] = (data[2]<<24) + (data[3]<<8) + ((data[4]&0xff00)>>8); + response[1] = (data[4]<<24) + (data[5]<<8) + ((data[6]&0xff00)>>8); + response[0] = (data[6]<<24) + (data[7]<<8) + ((data[8]&0xff00)>>8); + } + else + { + /* Response types 1, 1b, 3, 6 have the following structure: + * Types 4 and 5 are not supported. + * + * [47] Start bit - '0' + * [46] Transmission bit - '0' + * [45:40] R1, R1b, R6: Command index + * R3: Reserved - '111111' + * [39:8] R1, R1b: Card Status + * R3: OCR Register + * R6: [31:16] RCA + * [15: 0] Card Status Bits 23, 22, 19, 12:0 + * [23] COM_CRC_ERROR + * [22] ILLEGAL_COMMAND + * [19] ERROR + * [12:9] CURRENT_STATE + * [8] READY_FOR_DATA + * [7:6] + * [5] APP_CMD + * [4] + * [3] AKE_SEQ_ERROR + * [2] Reserved + * [1:0] Reserved for test mode + * [7:1] R1, R1b: CRC7 + * R3: Reserved - '1111111' + * [0] End Bit - '1' + */ + response[0] = (data[0]<<24) + (data[1]<<8) + ((data[2]&0xff00)>>8); } -} -bool sd_send_acommand(unsigned int cmd, unsigned long arg1, unsigned int arg2) -{ - unsigned int returncode; - if (sd_send_command(55, (card_info[current_card].rca)<<16, 1) == false) - return false; - sd_read_response(&returncode, 1); - if (sd_send_command(cmd, arg1, arg2) == false) - return false; return true; } -void sd_wait_for_state(tSDCardInfo* card, unsigned int state) +static void sd_wait_for_state(unsigned int state, unsigned int id) { unsigned int response = 0; - while(((response >> 9) & 0xf) != state) + + check_time[id] = USEC_TIMER; + + while (1) { - sd_send_command(SEND_STATUS, (card->rca) << 16, 1); + sd_command(SEND_STATUS, currcard->rca, &response, 1); + sd_check_timeout(0x80000, id); + + if (((response >> 9) & 0xf) == state) + break; + priority_yield(); - sd_read_response(&response, 1); - /* TODO: Add a timeout and error handling */ } + SD_STATE_REG = state; } - -STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount) - NOINLINE_ATTR ICODE_ATTR; - -STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount) +static inline void copy_read_sectors_fast(unsigned char **buf) { - unsigned int tmp = 0; - - if ( (unsigned long)buf & 1) - { /* not 16-bit aligned, copy byte by byte */ - unsigned char* bufend = buf + wordcount*2; - do - { - tmp = DATA_REG; - *buf++ = tmp & 0xff; - *buf++ = tmp >> 8; - } while (buf < bufend); /* tail loop is faster */ - } - else - { /* 16-bit aligned, can do faster copy */ - unsigned short* wbuf = (unsigned short*)buf; - unsigned short* wbufend = wbuf + wordcount; - do - { - *wbuf = DATA_REG; - } while (++wbuf < wbufend); /* tail loop is faster */ + /* Copy one chunk of 16 words using best method for start alignment */ + switch ( (intptr_t)*buf & 3 ) + { + case 0: + asm volatile ( + "ldmia %[data], { r2-r9 } \r\n" + "orr r2, r2, r3, lsl #16 \r\n" + "orr r4, r4, r5, lsl #16 \r\n" + "orr r6, r6, r7, lsl #16 \r\n" + "orr r8, r8, r9, lsl #16 \r\n" + "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" + "ldmia %[data], { r2-r9 } \r\n" + "orr r2, r2, r3, lsl #16 \r\n" + "orr r4, r4, r5, lsl #16 \r\n" + "orr r6, r6, r7, lsl #16 \r\n" + "orr r8, r8, r9, lsl #16 \r\n" + "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" + : [buf]"+&r"(*buf) + : [data]"r"(&DATA_REG) + : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9" + ); + break; + case 1: + asm volatile ( + "ldmia %[data], { r2-r9 } \r\n" + "orr r3, r2, r3, lsl #16 \r\n" + "strb r3, [%[buf]], #1 \r\n" + "mov r3, r3, lsr #8 \r\n" + "strh r3, [%[buf]], #2 \r\n" + "mov r3, r3, lsr #16 \r\n" + "orr r3, r3, r4, lsl #8 \r\n" + "orr r3, r3, r5, lsl #24 \r\n" + "mov r5, r5, lsr #8 \r\n" + "orr r5, r5, r6, lsl #8 \r\n" + "orr r5, r5, r7, lsl #24 \r\n" + "mov r7, r7, lsr #8 \r\n" + "orr r7, r7, r8, lsl #8 \r\n" + "orr r7, r7, r9, lsl #24 \r\n" + "mov r2, r9, lsr #8 \r\n" + "stmia %[buf]!, { r3, r5, r7 } \r\n" + "ldmia %[data], { r3-r10 } \r\n" + "orr r2, r2, r3, lsl #8 \r\n" + "orr r2, r2, r4, lsl #24 \r\n" + "mov r4, r4, lsr #8 \r\n" + "orr r4, r4, r5, lsl #8 \r\n" + "orr r4, r4, r6, lsl #24 \r\n" + "mov r6, r6, lsr #8 \r\n" + "orr r6, r6, r7, lsl #8 \r\n" + "orr r6, r6, r8, lsl #24 \r\n" + "mov r8, r8, lsr #8 \r\n" + "orr r8, r8, r9, lsl #8 \r\n" + "orr r8, r8, r10, lsl #24 \r\n" + "mov r10, r10, lsr #8 \r\n" + "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" + "strb r10, [%[buf]], #1 \r\n" + : [buf]"+&r"(*buf) + : [data]"r"(&DATA_REG) + : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" + ); + break; + case 2: + asm volatile ( + "ldmia %[data], { r2-r9 } \r\n" + "strh r2, [%[buf]], #2 \r\n" + "orr r3, r3, r4, lsl #16 \r\n" + "orr r5, r5, r6, lsl #16 \r\n" + "orr r7, r7, r8, lsl #16 \r\n" + "stmia %[buf]!, { r3, r5, r7 } \r\n" + "ldmia %[data], { r2-r8, r10 } \r\n" + "orr r2, r9, r2, lsl #16 \r\n" + "orr r3, r3, r4, lsl #16 \r\n" + "orr r5, r5, r6, lsl #16 \r\n" + "orr r7, r7, r8, lsl #16 \r\n" + "stmia %[buf]!, { r2, r3, r5, r7 } \r\n" + "strh r10, [%[buf]], #2 \r\n" + : [buf]"+&r"(*buf) + : [data]"r"(&DATA_REG) + : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" + ); + break; + case 3: + asm volatile ( + "ldmia %[data], { r2-r9 } \r\n" + "orr r3, r2, r3, lsl #16 \r\n" + "strb r3, [%[buf]], #1 \r\n" + "mov r3, r3, lsr #8 \r\n" + "orr r3, r3, r4, lsl #24 \r\n" + "mov r4, r4, lsr #8 \r\n" + "orr r5, r4, r5, lsl #8 \r\n" + "orr r5, r5, r6, lsl #24 \r\n" + "mov r6, r6, lsr #8 \r\n" + "orr r7, r6, r7, lsl #8 \r\n" + "orr r7, r7, r8, lsl #24 \r\n" + "mov r8, r8, lsr #8 \r\n" + "orr r2, r8, r9, lsl #8 \r\n" + "stmia %[buf]!, { r3, r5, r7 } \r\n" + "ldmia %[data], { r3-r10 } \r\n" + "orr r2, r2, r3, lsl #24 \r\n" + "mov r3, r3, lsr #8 \r\n" + "orr r4, r3, r4, lsl #8 \r\n" + "orr r4, r4, r5, lsl #24 \r\n" + "mov r5, r5, lsr #8 \r\n" + "orr r6, r5, r6, lsl #8 \r\n" + "orr r6, r6, r7, lsl #24 \r\n" + "mov r7, r7, lsr #8 \r\n" + "orr r8, r7, r8, lsl #8 \r\n" + "orr r8, r8, r9, lsl #24 \r\n" + "mov r9, r9, lsr #8 \r\n" + "orr r10, r9, r10, lsl #8 \r\n" + "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" + "strh r10, [%[buf]], #2 \r\n" + "mov r10, r10, lsr #16 \r\n" + "strb r10, [%[buf]], #1 \r\n" + : [buf]"+&r"(*buf) + : [data]"r"(&DATA_REG) + : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" + ); + break; } } -STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount) - NOINLINE_ATTR ICODE_ATTR; +static inline void copy_read_sectors_slow(unsigned char** buf) +{ + int cnt = FIFO_SIZE; + int t; -STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount) + /* Copy one chunk of 16 words */ + asm volatile ( + "1: \r\n" + "ldrh %[t], [%[data]] \r\n" + "strb %[t], [%[buf]], #1 \r\n" + "mov %[t], %[t], lsr #8 \r\n" + "strb %[t], [%[buf]], #1 \r\n" + "subs %[cnt], %[cnt], #1 \r\n" + "bgt 1b \r\n" + : [cnt]"+&r"(cnt), [buf]"+&r"(*buf), + [t]"=&r"(t) + : [data]"r"(&DATA_REG) + ); +} + +/* Writes have to be kept slow for now */ +static inline void copy_write_sectors(const unsigned char* buf) { unsigned short tmp = 0; - const unsigned char* bufend = buf + wordcount*2; + const unsigned char* bufend = buf + FIFO_SIZE*2; + do { - tmp = (unsigned short) *buf++; + tmp = (unsigned short) *buf++; tmp |= (unsigned short) *buf++ << 8; DATA_REG = tmp; } while (buf < bufend); /* tail loop is faster */ } - -void sd_select_bank(unsigned char bank) +static void sd_select_bank(unsigned char bank) { unsigned int response; unsigned char card_data[512]; unsigned char* write_buf; int i; - tSDCardInfo *card = &card_info[0]; /* Bank selection will only be done on - the onboard flash */ - if (current_bank != bank) + + memset(card_data, 0, 512); + sd_wait_for_state(TRAN, EC_TRAN_SEL_BANK); + BLOCK_SIZE_REG = 512; + BLOCK_COUNT_REG = 1; + sd_command(35, 0, &response, 0x1c0d); /* CMD35 is vendor specific */ + SD_STATE_REG = PRG; + + card_data[0] = bank; + + /* Write the card data */ + write_buf = card_data; + for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE) { - memset(card_data, 0, 512); - sd_wait_for_state(card, TRAN); - BLOCK_SIZE_REG = 512; - BLOCK_COUNT_REG = 1; - sd_send_command(35, 0, 0x1c0d); /* CMD35 is vendor specific */ - sd_read_response(&response, 1); - SD_STATE_REG = PRG; + /* Wait for the FIFO to empty */ + sd_poll_status(FIFO_EMPTY, 10000, EC_FIFO_SEL_BANK_EMPTY | DO_PANIC); - card_data[0] = bank; + copy_write_sectors(write_buf); /* Copy one chunk of 16 words */ - /* Write the card data */ - write_buf = card_data; - for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE) - { - /* Wait for the FIFO to be empty */ - while((STATUS_REG & FIFO_EMPTY) == 0) {} /* Erm... is this right? */ - - copy_write_sectors(write_buf, FIFO_SIZE); - - write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ - } - - while((STATUS_REG & DATA_DONE) == 0) {} - current_bank = bank; + write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ } + + sd_poll_status(DATA_DONE, 10000, EC_FIFO_SEL_BANK_DONE | DO_PANIC); + + currcard->current_bank = bank; } -void sd_init_device(void) +/* lock must already be aquired */ +static void sd_init_device(int card_no) { /* SD Protocol registers */ - unsigned int dummy; - int i; - - static unsigned int read_bl_len = 0; - static unsigned int c_size = 0; - static unsigned int c_size_mult = 0; - static unsigned long mult = 0; - + unsigned int i, dummy; + unsigned int c_size = 0; + unsigned long c_mult = 0; unsigned char carddata[512]; unsigned char *dataptr; - tSDCardInfo *card = &card_info[0]; /* Init onboard flash only */ - -/* Initialise card data as blank */ - card->initialized = false; - card->ocr = 0; - card->csd[0] = 0; - card->csd[1] = 0; - card->csd[2] = 0; - card->cid[0] = 0; - card->cid[1] = 0; - card->cid[2] = 0; - card->rca = 0; - - card->capacity = 0; - card->numblocks = 0; - card->block_size = 0; - card->block_exp = 0; /* Enable and initialise controller */ - GPIOG_ENABLE |= (0x3 << 5); - GPIOG_OUTPUT_EN |= (0x3 << 5); - GPIOG_OUTPUT_VAL |= (0x3 << 5); - outl(inl(0x70000088) & ~(0x4), 0x70000088); - outl(inl(0x7000008c) & ~(0x4), 0x7000008c); - outl(inl(0x70000080) | 0x4, 0x70000080); - outl(inl(0x70000084) | 0x4, 0x70000084); REG_1 = 6; - outl(inl(0x70000014) & ~(0x3ffff), 0x70000014); - outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014); - outl(0x1010, 0x70000034); - GPIOA_ENABLE |= (1 << 7); - GPIOA_OUTPUT_EN &= ~(1 << 7); - GPIOD_ENABLE |= (0x1f); - GPIOD_OUTPUT_EN |= (0x1f); - GPIOD_OUTPUT_VAL |= (0x1f); + currcard = &card_info[card_no]; + +/* Initialise card data as blank */ + memset(currcard, 0, sizeof(*currcard)); + + if (card_no == 0) + { + outl(inl(0x70000080) | 0x4, 0x70000080); + + GPIOA_ENABLE &= ~0x7a; + GPIOA_OUTPUT_EN &= ~0x7a; + GPIOD_ENABLE |= 0x1f; + GPIOD_OUTPUT_VAL |= 0x1f; + GPIOD_OUTPUT_EN |= 0x1f; + + outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014); + } + else + { + outl(inl(0x70000080) & ~0x4, 0x70000080); + + GPIOD_ENABLE &= ~0x1f; + GPIOD_OUTPUT_EN &= ~0x1f; + GPIOA_ENABLE |= 0x7a; + GPIOA_OUTPUT_VAL |= 0x7a; + GPIOA_OUTPUT_EN |= 0x7a; + + outl(inl(0x70000014) & ~(0x3ffff), 0x70000014); + } + + /* Init NAND */ + REG_11 |= (1 << 15); + REG_12 |= (1 << 15); + REG_12 &= ~(3 << 12); + REG_12 |= (1 << 13); + REG_11 &= ~(3 << 12); + REG_11 |= (1 << 13); + DEV_EN |= DEV_ATA; /* Enable controller */ DEV_RS |= DEV_ATA; /* Reset controller */ DEV_RS &=~DEV_ATA; /* Clear Reset */ -/* Init NAND */ - REG_11 |= (1 << 15); - REG_12 |= (1 << 15); - REG_12 &= ~(3 << 12); - REG_12 |= (1 << 13); - REG_11 &= ~(3 << 12); - REG_11 |= (1 << 13); - SD_STATE_REG = TRAN; + REG_5 = 0xf; - - sd_send_command(GO_IDLE_STATE, 0, 256); - while ((card->ocr & (1 << 31)) == 0) /* Loop until the card is powered up */ + sd_command(GO_IDLE_STATE, 0, &dummy, 256); + check_time[EC_POWER_UP] = USEC_TIMER; + while ((currcard->ocr & (1 << 31)) == 0) /* until card is powered up */ { - sd_send_acommand(SD_APP_OP_COND, 0x100000, 3); - sd_read_response(&(card->ocr), 3); - - if (card->ocr == 0) - { - /* TODO: Handle failure */ - while (1) {}; - } + sd_command(APP_CMD, currcard->rca, &dummy, 1); + sd_command(SD_APP_OP_COND, 0x100000, &currcard->ocr, 3); + sd_check_timeout(5000000, EC_POWER_UP); } - sd_send_command(ALL_SEND_CID, 0, 2); - sd_read_response(card->cid, 2); - sd_send_command(SEND_RELATIVE_ADDR, 0, 1); - sd_read_response(&card->rca, 1); - card->rca >>= 16; /* The Relative Card Address is the top 16 bits of the - 32 bits returned. Whenever it is used, it gets - shifted left by 16 bits, so this step could possibly - be skipped. */ + sd_command(ALL_SEND_CID, 0, currcard->cid, 2); + sd_command(SEND_RELATIVE_ADDR, 0, &currcard->rca, 1); + sd_command(SEND_CSD, currcard->rca, currcard->csd, 2); - sd_send_command(SEND_CSD, card->rca << 16, 2); - sd_read_response(card->csd, 2); - - /* Parse disk geometry */ /* These calculations come from the Sandisk SD card product manual */ - read_bl_len = ((card->csd[2] >> 16) & 0xf); - c_size = ((card->csd[2] & (0x3ff)) << 2) + - ((card->csd[1] & (0xc0000000)) >> 30); - c_size_mult = ((card->csd[1] >> 15) & 0x7); - mult = (1<<(c_size_mult + 2)); - card->max_read_bl_len = (1<block_size = BLOCK_SIZE; /* Always use 512 byte blocks */ - card->numblocks = (c_size + 1) * mult * (card->max_read_bl_len / 512); - card->capacity = card->numblocks * card->block_size; + c_size = ((currcard->csd[2] & 0x3ff) << 2) + (currcard->csd[1] >> 30) + 1; + c_mult = 4 << ((currcard->csd[1] >> 15) & 7); + currcard->max_read_bl_len = 1 << ((currcard->csd[2] >> 16) & 15); + currcard->block_size = BLOCK_SIZE; /* Always use 512 byte blocks */ + currcard->numblocks = c_size * c_mult * (currcard->max_read_bl_len / 512); + currcard->capacity = currcard->numblocks * currcard->block_size; REG_1 = 0; - sd_send_command(SELECT_CARD, card->rca << 16, 129); - sd_read_response(&dummy, 1); /* I don't think we use the result from this */ - sd_send_acommand(SET_BUS_WIDTH, (card->rca << 16) | 2, 1); - sd_read_response(&dummy, 1); /* 4 bit wide bus */ - sd_send_command(SET_BLOCKLEN, card->block_size, 1); - sd_read_response(&dummy, 1); - BLOCK_SIZE_REG = card->block_size; + + sd_command(SELECT_CARD, currcard->rca , &dummy, 129); + sd_command(APP_CMD, currcard->rca , &dummy, 1); + sd_command(SET_BUS_WIDTH, currcard->rca | 2 , &dummy, 1); /* 4 bit */ + sd_command(SET_BLOCKLEN, currcard->block_size, &dummy, 1); + BLOCK_SIZE_REG = currcard->block_size; /* If this card is > 4Gb, then we need to enable bank switching */ - if(card->numblocks >= BLOCKS_PER_BANK) + if(currcard->numblocks >= BLOCKS_PER_BANK) { SD_STATE_REG = TRAN; BLOCK_COUNT_REG = 1; - sd_send_command(SWITCH_FUNC, 0x80ffffef, 0x1c05); - sd_read_response(&dummy, 1); + sd_command(SWITCH_FUNC, 0x80ffffef, &dummy, 0x1c05); /* Read 512 bytes from the card. The first 512 bits contain the status information TODO: Do something useful with this! */ @@ -436,206 +550,216 @@ void sd_init_device(void) for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE) { /* Wait for the FIFO to be full */ - while((STATUS_REG & FIFO_FULL) == 0) {} - - copy_read_sectors(dataptr, FIFO_SIZE); - - dataptr += (FIFO_SIZE*2); /* Advance one chunk of 16 words */ + sd_poll_status(FIFO_FULL, 100000, + EC_FIFO_ENA_BANK_EMPTY | DO_PANIC); + copy_read_sectors_slow(&dataptr); } } - spinlock_init(&sd_mtx); + + currcard->initialized = true; } /* API Functions */ void ata_led(bool onoff) { - (void)onoff; + led(onoff); } -int ata_read_sectors(IF_MV2(int drive,) - unsigned long start, - int incount, +int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount, void* inbuf) { - int ret = 0; - long timeout; - int count; - void* buf; - long spinup_start; + int ret = 0; + unsigned char *buf, *buf_end; unsigned int dummy; - unsigned int response; - unsigned int i; - tSDCardInfo *card = &card_info[current_card]; - + int bank; + /* TODO: Add DMA support. */ -#ifdef HAVE_MULTIVOLUME - (void)drive; /* unused for now */ -#endif spinlock_lock(&sd_mtx); - last_disk_activity = current_tick; - spinup_start = current_tick; - - ata_enable(true); ata_led(true); - timeout = current_tick + READ_TIMEOUT; - - /* TODO: Select device */ - if(current_card == 0) + if (drive != 0 && (GPIOA_INPUT_VAL & 0x80) != 0) { - if(start >= BLOCKS_PER_BANK) - { - sd_select_bank(1); - start -= BLOCKS_PER_BANK; - } else { - sd_select_bank(0); - } + /* no external sd-card inserted */ + ret = -9; + goto ata_read_error; } - buf = inbuf; - count = incount; - while (TIME_BEFORE(current_tick, timeout)) { - ret = 0; - last_disk_activity = current_tick; + if (&card_info[drive] != currcard || !card_info[drive].initialized) + sd_init_device(drive); - SD_STATE_REG = TRAN; - BLOCK_COUNT_REG = count; - sd_send_command(READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, 0x1c25); - sd_read_response(&dummy, 1); - /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */ + last_disk_activity = current_tick; - for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE) - { - /* Wait for the FIFO to be full */ - while((STATUS_REG & FIFO_FULL) == 0) {} + bank = start / BLOCKS_PER_BANK; - copy_read_sectors(buf, FIFO_SIZE); + if (currcard->current_bank != bank) + sd_select_bank(bank); - buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ + start -= bank * BLOCKS_PER_BANK; - /* TODO: Switch bank if necessary */ + sd_wait_for_state(TRAN, EC_TRAN_READ_ENTRY); + BLOCK_COUNT_REG = incount; + sd_command(READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, &dummy, 0x1c25); + /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */ - last_disk_activity = current_tick; - } - udelay(75); - sd_send_command(STOP_TRANSMISSION, 0, 1); - sd_read_response(&dummy, 1); + buf_end = (unsigned char *)inbuf + incount * currcard->block_size; + for (buf = inbuf; buf < buf_end;) + { + /* Wait for the FIFO to be full */ + sd_poll_status(FIFO_FULL, 0x80000, EC_FIFO_READ_FULL | DO_PANIC); + copy_read_sectors_fast(&buf); /* Copy one chunk of 16 words */ - response = 0; - sd_wait_for_state(card, TRAN); - break; + /* TODO: Switch bank if necessary */ } + + last_disk_activity = current_tick; +#if 0 + udelay(75); +#endif + sd_command(STOP_TRANSMISSION, 0, &dummy, 1); + sd_wait_for_state(TRAN, EC_TRAN_READ_EXIT); + +ata_read_error: ata_led(false); - ata_enable(false); spinlock_unlock(&sd_mtx); return ret; } - -int ata_write_sectors(IF_MV2(int drive,) - unsigned long start, - int count, - const void* buf) +int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, + const void* outbuf) { /* Write support is not finished yet */ -/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks +/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks to improve performance */ unsigned int response; - void const* write_buf; + void const* buf, *buf_end; int ret = 0; - unsigned int i; - long timeout; - tSDCardInfo *card = &card_info[current_card]; + int bank; spinlock_lock(&sd_mtx); - ata_enable(true); + ata_led(true); - if(current_card == 0) + + if (drive != 0 && (GPIOA_INPUT_VAL & 0x80) != 0) { - if(start < BLOCKS_PER_BANK) - { - sd_select_bank(0); - } else { - sd_select_bank(1); - start -= BLOCKS_PER_BANK; - } + /* no external sd-card inserted */ + ret = -9; + goto error; } -retry: - sd_wait_for_state(card, TRAN); + if (&card_info[drive] != currcard || !card_info[drive].initialized) + sd_init_device(drive); + + bank = start / BLOCKS_PER_BANK; + + if (currcard->current_bank != bank) + sd_select_bank(bank); + + start -= bank * BLOCKS_PER_BANK; + + check_time[EC_WRITE_TIMEOUT] = USEC_TIMER; + sd_wait_for_state(TRAN, EC_TRAN_WRITE_ENTRY); BLOCK_COUNT_REG = count; - sd_send_command(WRITE_MULTIPLE_BLOCK, start * SECTOR_SIZE, 0x1c2d); - sd_read_response(&response, 1); - write_buf = buf; - for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE) + sd_command(WRITE_MULTIPLE_BLOCK, start * SECTOR_SIZE, &response, 0x1c2d); + + buf_end = outbuf + count * currcard->block_size; + for (buf = outbuf; buf < buf_end; buf += 2 * FIFO_SIZE) { - if(i >= (count * card->block_size / 2)-FIFO_SIZE) + if (buf >= buf_end - 2 * FIFO_SIZE) { /* Set SD_STATE_REG to PRG for the last buffer fill */ SD_STATE_REG = PRG; } - /* Wait for the FIFO to be empty */ - while((STATUS_REG & FIFO_EMPTY) == 0) {} - /* Perhaps we could use bit 8 of card status (READY_FOR_DATA)? */ + udelay(2); /* needed here (loop is too fast :-) */ - copy_write_sectors(write_buf, FIFO_SIZE); + /* Wait for the FIFO to empty */ + sd_poll_status(FIFO_EMPTY, 0x80000, EC_FIFO_WR_EMPTY | DO_PANIC); + + copy_write_sectors(buf); /* Copy one chunk of 16 words */ - write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ /* TODO: Switch bank if necessary */ - - last_disk_activity = current_tick; } - timeout = current_tick + WRITE_TIMEOUT; + last_disk_activity = current_tick; - while((STATUS_REG & DATA_DONE) == 0) { - if(current_tick >= timeout) - { - sd_send_command(STOP_TRANSMISSION, 0, 1); - sd_read_response(&response, 1); - goto retry; - } - } - sd_send_command(STOP_TRANSMISSION, 0, 1); - sd_read_response(&response, 1); + sd_poll_status(DATA_DONE, 0x80000, EC_FIFO_WR_DONE | DO_PANIC); + sd_check_timeout(0x80000, EC_WRITE_TIMEOUT); - sd_wait_for_state(card, TRAN); + sd_command(STOP_TRANSMISSION, 0, &response, 1); + sd_wait_for_state(TRAN, EC_TRAN_WRITE_EXIT); + + error: ata_led(false); - ata_enable(false); spinlock_unlock(&sd_mtx); return ret; } +static void sd_thread(void) __attribute__((noreturn)); static void sd_thread(void) { struct event ev; bool idle_notified = false; - while (1) { + while (1) + { queue_wait_w_tmo(&sd_queue, &ev, HZ); + switch ( ev.id ) { - default: - if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) - { - idle_notified = false; - } - else - { - if (!idle_notified) - { - call_ata_idle_notifys(false); - idle_notified = true; - } - } - break; + case SD_HOTSWAP: + { + int status = 0; + enum { SD_UNMOUNTED = 0x1, SD_MOUNTED = 0x2 }; + + /* Delay on insert and remove to prevent reading state if it is + just bouncing back and forth while card is sliding - delay on + insert is also required for the card to stabilize and accept + commands */ + sleep(HZ/10); + + /* Lock to keep us from messing with this variable while an init + may be in progress */ + spinlock_lock(&sd_mtx); + card_info[1].initialized = false; + spinlock_unlock(&sd_mtx); + + /* Either unmount because the card was pulled or unmount and + remount if already mounted since multiple messages may be + generated for the same event - like someone inserting a new + card before anything detects the old one pulled :) */ + if (disk_unmount(1) != 0) /* release "by force" */ + status |= SD_UNMOUNTED; + + if (card_detect_target() && disk_mount(1) != 0) /* mount SD-CARD */ + status |= SD_MOUNTED; + + if (status & SD_UNMOUNTED) + queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); + + if (status & SD_MOUNTED) + queue_broadcast(SYS_HOTSWAP_INSERTED, 0); + + if (status) + queue_broadcast(SYS_FS_CHANGED, 0); + break; + } /* SD_HOTSWAP */ + case SYS_TIMEOUT: + if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) + { + idle_notified = false; + } + else if (!idle_notified) + { + call_ata_idle_notifys(false); + idle_notified = true; + } + break; } } } @@ -648,7 +772,7 @@ void ata_spindown(int seconds) bool ata_disk_is_active(void) { - return 0; + return 0; } void ata_sleep(void) @@ -662,12 +786,12 @@ void ata_spin(void) /* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */ int ata_hard_reset(void) { - return 0; + return 0; } int ata_soft_reset(void) { - return 0; + return 0; } void ata_enable(bool on) @@ -682,22 +806,113 @@ void ata_enable(bool on) } } -unsigned short* ata_get_identify(void) -{ - return identify_info; -} - int ata_init(void) { - sd_init_device(); - if ( !initialized ) + ata_led(false); + + /* NOTE: This init isn't dual core safe */ + if (!initialized) { - queue_init(&sd_queue, true); - create_thread(sd_thread, sd_stack, - sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM) - IF_COP(, CPU, false)); initialized = true; + + spinlock_init(&sd_mtx); + + spinlock_lock(&sd_mtx); + + /* init controller */ + outl(inl(0x70000088) & ~(0x4), 0x70000088); + outl(inl(0x7000008c) & ~(0x4), 0x7000008c); + outl(inl(0x70000084) | 0x4, 0x70000084); + outl(0x1010, 0x70000034); + + GPIOG_ENABLE |= (0x3 << 5); + GPIOG_OUTPUT_EN |= (0x3 << 5); + GPIOG_OUTPUT_VAL |= (0x3 << 5); + + /* enable card detection port - mask interrupt first */ + GPIOA_INT_EN &= ~0x80; + + GPIOA_OUTPUT_EN &= ~0x80; + GPIOA_ENABLE |= 0x80; + + sd_init_device(0); + + queue_init(&sd_queue, true); + create_thread(sd_thread, sd_stack, sizeof(sd_stack), + sd_thread_name IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU, false)); + + /* enable interupt for the mSD card */ + sleep(HZ/10); + + CPU_INT_EN = HI_MASK; + CPU_HI_INT_EN = GPIO0_MASK; + + sd1_status = GPIOA_INPUT_VAL & 0x80; + GPIOA_INT_LEV = (GPIOA_INT_LEV & ~0x80) | (sd1_status ^ 0x80); + + GPIOA_INT_CLR = 0x80; + GPIOA_INT_EN |= 0x80; + + spinlock_unlock(&sd_mtx); } return 0; } + +/* move the sd-card info to mmc struct */ +tCardInfo *card_get_info_target(int card_no) +{ + int i, temp; + static tCardInfo card; + static const char mantissa[] = { /* *10 */ + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; + static const int exponent[] = { /* use varies */ + 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 }; + + card.initialized = card_info[card_no].initialized; + card.ocr = card_info[card_no].ocr; + for(i=0; i<4; i++) card.csd[i] = card_info[card_no].csd[3-i]; + for(i=0; i<4; i++) card.cid[i] = card_info[card_no].cid[3-i]; + card.numblocks = card_info[card_no].numblocks; + card.blocksize = card_info[card_no].block_size; + card.size = card_info[card_no].capacity < 0xffffffff ? + card_info[card_no].capacity : 0xffffffff; + card.block_exp = card_info[card_no].block_exp; + temp = card_extract_bits(card.csd, 29, 3); + card.speed = mantissa[card_extract_bits(card.csd, 25, 4)] + * exponent[temp > 2 ? 7 : temp + 4]; + card.nsac = 100 * card_extract_bits(card.csd, 16, 8); + temp = card_extract_bits(card.csd, 13, 3); + card.tsac = mantissa[card_extract_bits(card.csd, 9, 4)] + * exponent[temp] / 10; + card.cid[0] = htobe32(card.cid[0]); /* ascii chars here */ + card.cid[1] = htobe32(card.cid[1]); /* ascii chars here */ + temp = *((char*)card.cid+13); /* adjust year<=>month, 1997 <=> 2000 */ + *((char*)card.cid+13) = (unsigned char)((temp >> 4) | (temp << 4)) + 3; + + return &card; +} + +bool card_detect_target(void) +{ + /* 0x00:inserted, 0x80:not inserted */ + return (GPIOA_INPUT_VAL & 0x80) == 0; +} + +/* called on insertion/removal interrupt */ +void microsd_int(void) +{ + int status = GPIOA_INPUT_VAL & 0x80; + + GPIOA_INT_LEV = (GPIOA_INT_LEV & ~0x80) | (status ^ 0x80); + GPIOA_INT_CLR = 0x80; + + if (status == sd1_status) + return; + + sd1_status = status; + + /* Take final state only - insert/remove is bouncy */ + queue_remove_from_head(&sd_queue, SD_HOTSWAP); + queue_post(&sd_queue, SD_HOTSWAP, status); +} diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-target.h b/firmware/target/arm/sandisk/sansa-e200/ata-target.h index dfdd3fe6ae..61884e0864 100644 --- a/firmware/target/arm/sandisk/sansa-e200/ata-target.h +++ b/firmware/target/arm/sandisk/sansa-e200/ata-target.h @@ -19,25 +19,4 @@ #ifndef ATA_TARGET_H #define ATA_TARGET_H -#include "inttypes.h" - -typedef struct -{ - bool initialized; - - unsigned int ocr; /* OCR register */ - unsigned int csd[4]; /* CSD register */ - unsigned int cid[4]; /* CID register */ - unsigned int rca; - - uint64_t capacity; /* size in bytes */ - unsigned long numblocks; /* size in flash blocks */ - unsigned int block_size; /* block size in bytes */ - unsigned int max_read_bl_len;/* max read data block length */ - unsigned int block_exp; /* block size exponent */ -} tSDCardInfo; - -tSDCardInfo *sd_card_info(int card_no); -bool sd_touched(void); - #endif diff --git a/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h b/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h new file mode 100644 index 0000000000..95cb26d2f7 --- /dev/null +++ b/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Antonius Hellmann + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef HOTSWAP_TARGET_H +#define HOTSWAP_TARGET_H + +#include "inttypes.h" +#include "hotswap.h" + +typedef struct +{ + bool initialized; + + unsigned int ocr; /* OCR register */ + unsigned int csd[4]; /* CSD register */ + unsigned int cid[4]; /* CID register */ + unsigned int rca; + + uint64_t capacity; /* size in bytes */ + unsigned long numblocks; /* size in flash blocks */ + unsigned int block_size; /* block size in bytes */ + unsigned int max_read_bl_len;/* max read data block length */ + unsigned int block_exp; /* block size exponent */ + unsigned char current_bank; /* The bank that we are working with */ +} tSDCardInfo; + +tCardInfo *card_get_info_target(int card_no); +bool card_detect_target(void); + +#endif diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c index ef0c62dcf5..49ffdd300b 100644 --- a/firmware/target/arm/system-pp502x.c +++ b/firmware/target/arm/system-pp502x.c @@ -56,8 +56,11 @@ void irq(void) /* TODO: this should really be in the target tree, but moving it there caused crt0.S not to find it while linking */ /* TODO: Even if it isn't in the target tree, this should be the default case */ +#ifdef SANSA_E200 extern void button_int(void); extern void clickwheel_int(void); +extern void microsd_int(void); +#endif void irq(void) { @@ -65,11 +68,15 @@ void irq(void) if (CPU_INT_STAT & TIMER1_MASK) { TIMER1(); } - else if (CPU_INT_STAT & TIMER2_MASK) + else if (CPU_INT_STAT & TIMER2_MASK) { TIMER2(); + } #ifdef SANSA_E200 - else if (CPU_HI_INT_STAT & GPIO1_MASK) - { + else if (CPU_HI_INT_STAT & GPIO0_MASK) { + if (GPIOA_INT_STAT & 0x80) + microsd_int(); + } + else if (CPU_HI_INT_STAT & GPIO1_MASK) { if (GPIOF_INT_STAT & 0xff) button_int(); if (GPIOH_INT_STAT & 0xc0) diff --git a/firmware/usb.c b/firmware/usb.c index 217aaf8b84..9e9cb77919 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -255,8 +255,8 @@ static void usb_thread(void) break; #ifdef HAVE_MMC - case SYS_MMC_INSERTED: - case SYS_MMC_EXTRACTED: + case SYS_HOTSWAP_INSERTED: + case SYS_HOTSWAP_EXTRACTED: if(usb_state == USB_INSERTED) { usb_enable(false);