summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/tms320dm320/sdmmc-dm320.c
diff options
context:
space:
mode:
authorTomasz Moń <desowin@gmail.com>2011-11-16 14:08:01 +0000
committerTomasz Moń <desowin@gmail.com>2011-11-16 14:08:01 +0000
commite8a8a1be43afe63079ae48ce1a9eb3052df3b1a4 (patch)
tree084e1cdf27a339ce58e24cff8fec8c31432b52db /firmware/target/arm/tms320dm320/sdmmc-dm320.c
parent992d4eb775cac48e107e18d72783ebfb39c4234f (diff)
downloadrockbox-e8a8a1be43afe63079ae48ce1a9eb3052df3b1a4.tar.gz
rockbox-e8a8a1be43afe63079ae48ce1a9eb3052df3b1a4.zip
Sandisk Sansa Connect port (FS #12363)
Included are drivers for buttons, backlight, lcd, audio and storage. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31000 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/tms320dm320/sdmmc-dm320.c')
-rw-r--r--firmware/target/arm/tms320dm320/sdmmc-dm320.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/firmware/target/arm/tms320dm320/sdmmc-dm320.c b/firmware/target/arm/tms320dm320/sdmmc-dm320.c
new file mode 100644
index 0000000000..307b90ec3b
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sdmmc-dm320.c
@@ -0,0 +1,949 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2011 by Tomasz Moń
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "sd.h"
+#include "system.h"
+#include <string.h>
+#include "gcc_extensions.h"
+#include "thread.h"
+#include "panic.h"
+#include "kernel.h"
+#include "dma-target.h"
+
+//#define SD_DEBUG
+
+#ifdef SD_DEBUG
+#include "lcd-target.h"
+#include "lcd.h"
+#include "font.h"
+#ifdef BOOTLOADER
+#include "common.h"
+#else
+#include "debug.h"
+#endif
+#endif
+#include "sdmmc.h"
+#include "disk.h"
+#include "fat.h"
+#include "system-target.h"
+
+/* The configuration method is not very flexible. */
+#define CARD_NUM_SLOT 1
+#define NUM_CARDS 2
+
+#define EC_OK 0
+#define EC_FAILED 1
+#define EC_NOCARD 2
+#define EC_WAIT_STATE_FAILED 3
+#define EC_POWER_UP 4
+#define EC_FIFO_WR_EMPTY 5
+#define EC_FIFO_WR_DONE 6
+#define EC_TRAN_READ_ENTRY 7
+#define EC_TRAN_READ_EXIT 8
+#define EC_TRAN_WRITE_ENTRY 9
+#define EC_TRAN_WRITE_EXIT 10
+#define EC_COMMAND 11
+#define EC_WRITE_PROTECT 12
+#define EC_DATA_TIMEOUT 13
+#define EC_RESP_TIMEOUT 14
+#define EC_CRC_ERROR 15
+#define NUM_EC 16
+
+#define MIN_YIELD_PERIOD 1000
+#define UNALIGNED_NUM_SECTORS 10
+#define MAX_TRANSFER_ERRORS 10
+
+#define SECTOR_SIZE 512
+#define BLOCKS_PER_BANK 0x7A7800
+
+/* command flags for send_cmd */
+#define SDHC_RESP_FMT_NONE 0x0000
+#define SDHC_RESP_FMT_1 0x0200
+#define SDHC_RESP_FMT_2 0x0400
+#define SDHC_RESP_FMT_3 0x0600
+
+#define INITIAL_CLK 312500 /* Initial clock */
+#define SD_CLK 24000000 /* Clock for SD cards */
+#define MMC_CLK 15000000 /* Clock for MMC cards */
+
+#ifdef SD_DEBUG
+#ifdef BOOTLOADER
+#define dbgprintf printf
+#else
+#define dbgprintf DEBUGF
+#endif
+#else
+#define dbgprintf(...)
+#endif
+
+struct sd_card_status
+{
+ int retry;
+ int retry_max;
+};
+
+/** static, private data **/
+
+/* for compatibility */
+static long last_disk_activity = -1;
+
+static bool initialized = false;
+static unsigned int sd_thread_id = 0;
+
+static bool sd_enabled = false;
+static long next_yield = 0;
+
+static tCardInfo card_info [NUM_CARDS];
+static tCardInfo *currcard;
+
+static struct sd_card_status sd_status[NUM_CARDS] =
+{
+#if NUM_CARDS > 1
+ {0, 10},
+#endif
+ {0, 10}
+};
+
+/* Shoot for around 75% usage */
+static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)];
+static const char sd_thread_name[] = "sd";
+static struct mutex sd_mtx SHAREDBSS_ATTR;
+static struct event_queue sd_queue;
+static volatile unsigned int transfer_error[NUM_DRIVES];
+/* align on cache line size */
+static unsigned char aligned_buffer[UNALIGNED_NUM_SECTORS * SD_BLOCK_SIZE]
+ __attribute__((aligned(32)));
+
+static void sd_card_mux(int card_no)
+{
+#ifdef HAVE_MULTIDRIVE
+#ifdef SANSA_CONNECT
+ /* GIO6 - select Card; GIO5 - select iNAND (both active low) */
+ if (card_no == CARD_NUM_SLOT)
+ {
+ IO_GIO_BITSET0 = (1 << 5); /* deselect iNAND (GIO5) */
+ IO_GIO_BITCLR0 = (1 << 6); /* select card (GIO6) */
+ }
+ else
+ {
+ IO_GIO_BITSET0 = (1 << 6); /* deselect card (GIO6) */
+ IO_GIO_BITCLR0 = (1 << 5); /* select iNAND (GIO5) */
+ }
+#else /* Different players */
+ (void)card_no;
+#endif
+#else /* No multidrive */
+ (void)card_no;
+#endif
+}
+
+
+void sd_enable(bool on)
+{
+ if (sd_enabled == on)
+ return; /* nothing to do */
+
+ if (on)
+ {
+ sd_enabled = true;
+ }
+ else
+ {
+ sd_enabled = false;
+ }
+}
+
+/* sets clock rate just like OF does */
+static void sd_set_clock_rate(unsigned long rate)
+{
+ unsigned char rate_val = 0;
+
+ if (rate == INITIAL_CLK)
+ {
+ rate_val = 0x3B;
+ }
+ else if (rate > INITIAL_CLK)
+ {
+ rate_val = 0;
+ }
+ else
+ {
+ rate_val = 0xFF;
+ }
+
+ IO_MMC_MEM_CLK_CONTROL = (IO_MMC_MEM_CLK_CONTROL & 0xFF00) | rate_val;
+}
+
+static int sd_poll_status(int st_reg_num, volatile unsigned int flag)
+{
+ unsigned int status;
+ unsigned int status1;
+ bool done;
+
+ do
+ {
+ long time = current_tick;
+
+ if (TIME_AFTER(time, next_yield))
+ {
+ long ty = current_tick;
+ yield();
+ next_yield = ty + MIN_YIELD_PERIOD;
+ }
+
+ status = IO_MMC_STATUS0;
+ status1 = IO_MMC_STATUS1;
+
+ if (status & MMC_ST0_CMD_TIMEOUT)
+ {
+ dbgprintf("CMD timeout");
+ return -EC_RESP_TIMEOUT;
+ }
+ if (status & MMC_ST0_DATA_TIMEOUT)
+ {
+ dbgprintf("DATA timeout");
+ return -EC_DATA_TIMEOUT;
+ }
+
+ if (status &
+ (MMC_ST0_WR_CRCERR | MMC_ST0_RD_CRCERR | MMC_ST0_RESP_CRCERR))
+ {
+ dbgprintf("CRC error");
+ return -EC_CRC_ERROR;
+ }
+
+ if (st_reg_num == 0)
+ {
+ done = status & flag;
+ }
+ else
+ {
+ done = status1 & flag;
+ }
+ } while (!done);
+
+ return EC_OK;
+}
+
+static int dma_wait_for_completion(void)
+{
+ unsigned short dma_status;
+
+ do
+ {
+ long time = current_tick;
+
+ if (TIME_AFTER(time, next_yield))
+ {
+ long ty = current_tick;
+ yield();
+ next_yield = ty + MIN_YIELD_PERIOD;
+ }
+
+ dma_status = IO_MMC_SD_DMA_STATUS1;
+ if (dma_status & (1 << 13))
+ {
+ return -EC_DATA_TIMEOUT;
+ }
+ } while (dma_status & (1 << 12));
+
+ return EC_OK;
+}
+
+static int sd_command(int cmd, unsigned long arg,
+ int cmdat, unsigned long *response)
+{
+ int ret;
+
+ /* Clear response registers */
+ IO_MMC_RESPONSE0 = 0;
+ IO_MMC_RESPONSE1 = 0;
+ IO_MMC_RESPONSE2 = 0;
+ IO_MMC_RESPONSE3 = 0;
+ IO_MMC_RESPONSE4 = 0;
+ IO_MMC_RESPONSE5 = 0;
+ IO_MMC_RESPONSE6 = 0;
+ IO_MMC_RESPONSE7 = 0;
+ IO_MMC_COMMAND_INDEX = 0;
+ IO_MMC_SPI_DATA = 0;
+
+ IO_MMC_ARG_LOW = (unsigned int)((arg & 0xFFFF));
+ IO_MMC_ARG_HI = (unsigned int)((arg & 0xFFFF0000) >> 16);
+
+ /* SD is always in push-pull mode */
+ cmdat |= MMC_CMD_PPLEN;
+
+ cmdat |= (cmd & MMC_CMD_CMD_MASK);
+
+ if (cmdat & MMC_CMD_DATA)
+ cmdat |= MMC_CMD_DCLR;
+
+ IO_MMC_COMMAND = cmdat;
+
+ if (cmdat & MMC_CMD_DATA)
+ {
+ /* Command requires data - do not wait for RSPDNE */
+ ret = EC_OK;
+ }
+ else
+ {
+ ret = sd_poll_status(0, MMC_ST0_RSPDNE);
+ }
+
+ if (ret != EC_OK)
+ {
+ dbgprintf("Command failed (ret %d)", ret);
+ return ret;
+ }
+
+ if (response == NULL)
+ {
+ /* discard response */
+ }
+ else if ((cmdat & SDHC_RESP_FMT_1) || (cmdat & SDHC_RESP_FMT_3))
+ {
+ response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
+ }
+ else if (cmdat & SDHC_RESP_FMT_2)
+ {
+ response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
+ response[1] = (IO_MMC_RESPONSE5 << 16) | IO_MMC_RESPONSE4;
+ response[2] = (IO_MMC_RESPONSE3 << 16) | IO_MMC_RESPONSE2;
+ response[3] = (IO_MMC_RESPONSE1 << 16) | IO_MMC_RESPONSE0;
+ }
+
+ return 0;
+}
+
+static int sd_init_card(const int card_no)
+{
+ bool sdhc = false;
+ unsigned long response[4];
+ int ret;
+ int i;
+
+ memset(currcard, 0, sizeof(*currcard));
+ sd_card_mux(card_no);
+
+ /* Set data bus width to 1 bit */
+ bitclr16(&IO_MMC_CONTROL, MMC_CTRL_WIDTH);
+ sd_set_clock_rate(INITIAL_CLK);
+
+ ret = sd_command(SD_GO_IDLE_STATE, 0, MMC_CMD_INITCLK, NULL);
+
+ if (ret < 0)
+ return -1;
+
+ ret = sd_command(SD_SEND_IF_COND, 0x1AA,
+ SDHC_RESP_FMT_3, response);
+ if ((response[0] & 0xFFF) == 0x1AA)
+ {
+ sdhc = true;
+ dbgprintf("found sdhc card");
+ }
+
+ while ((currcard->ocr & (1 << 31)) == 0) /* until card is powered up */
+ {
+ ret = sd_command(SD_APP_CMD, currcard->rca,
+ SDHC_RESP_FMT_1, NULL);
+ if (ret < 0)
+ {
+ dbgprintf("SD_APP_CMD failed");
+ return -1;
+ }
+
+ ret = sd_command(SD_APP_OP_COND,
+ (1 << 20) /* 3.2-3.3V */ |
+ (1 << 21) /* 3.3-3.4V */ |
+ (sdhc ? (1 << 30) : 0),
+ SDHC_RESP_FMT_3, &currcard->ocr);
+
+ if (ret < 0)
+ {
+ dbgprintf("SD_APP_OP_COND failed");
+ return -1;
+ }
+ }
+
+ dbgprintf("Card powered up");
+
+ ret = sd_command(SD_ALL_SEND_CID, 0,
+ SDHC_RESP_FMT_2, response);
+ if (ret < 0)
+ {
+ dbgprintf("SD_ALL_SEND_CID failed");
+ return -1;
+ }
+
+ for (i = 0; i<4; i++)
+ {
+ currcard->cid[i] = response[i];
+ }
+
+ ret = sd_command(SD_SEND_RELATIVE_ADDR, 0,
+ SDHC_RESP_FMT_1, &currcard->rca);
+ if (ret < 0)
+ {
+ dbgprintf("SD_SEND_RELATIVE_ADDR failed");
+ return -1;
+ }
+
+ ret = sd_command(SD_SEND_CSD, currcard->rca,
+ SDHC_RESP_FMT_2, response);
+ if (ret < 0)
+ {
+ dbgprintf("SD_SEND_CSD failed");
+ return -1;
+ }
+
+ for (i = 0; i<4; i++)
+ {
+ currcard->csd[i] = response[i];
+ }
+
+ sd_parse_csd(currcard);
+
+ sd_set_clock_rate(currcard->speed);
+
+ ret = sd_command(SD_SELECT_CARD, currcard->rca,
+ SDHC_RESP_FMT_1, NULL);
+ if (ret < 0)
+ {
+ dbgprintf("SD_SELECT_CARD failed");
+ return -1;
+ }
+
+ ret = sd_command(SD_APP_CMD, currcard->rca,
+ SDHC_RESP_FMT_1, NULL);
+ if (ret < 0)
+ {
+ dbgprintf("SD_APP_CMD failed");
+ return -1;
+ }
+
+ ret = sd_command(SD_SET_BUS_WIDTH, currcard->rca | 2,
+ SDHC_RESP_FMT_1, NULL); /* 4 bit */
+ if (ret < 0)
+ {
+ dbgprintf("SD_SET_BUS_WIDTH failed");
+ return -1;
+ }
+
+ /* Set data bus width to 4 bits */
+ bitset16(&IO_MMC_CONTROL, MMC_CTRL_WIDTH);
+
+ ret = sd_command(SD_SET_BLOCKLEN, currcard->blocksize,
+ SDHC_RESP_FMT_1, NULL);
+ if (ret < 0)
+ {
+ dbgprintf("SD_SET_BLOCKLEN failed");
+ return -1;
+ }
+
+ IO_MMC_BLOCK_LENGTH = currcard->blocksize;
+
+ dbgprintf("Card initialized");
+ currcard->initialized = 1;
+
+ return EC_OK;
+}
+
+/* lock must already by aquired */
+static void sd_select_device(int card_no)
+{
+ currcard = &card_info[card_no];
+
+ if (card_no == 0)
+ {
+ /* Main card always gets a chance */
+ sd_status[0].retry = 0;
+ }
+
+ if (currcard->initialized > 0)
+ {
+ /* This card is already initialized - switch to it */
+ sd_card_mux(card_no);
+ return;
+ }
+
+ if (currcard->initialized == 0)
+ {
+ /* Card needs (re)init */
+ sd_init_card(card_no);
+ }
+}
+
+static inline bool card_detect_target(void)
+{
+#ifdef SANSA_CONNECT
+ bool removed;
+
+ removed = IO_GIO_BITSET0 & (1 << 14);
+
+ return !removed;
+#else
+ return false;
+#endif
+}
+
+
+#ifdef HAVE_HOTSWAP
+
+static int sd1_oneshot_callback(struct timeout *tmo)
+{
+ (void)tmo;
+
+ /* This is called only if the state was stable for 300ms - check state
+ * and post appropriate event. */
+ if (card_detect_target())
+ {
+ queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
+ }
+ else
+ queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
+ return 0;
+}
+
+#ifdef SANSA_CONNECT
+void GIO14(void) __attribute__ ((section(".icode")));
+void GIO14(void)
+{
+ static struct timeout sd1_oneshot;
+
+ /* clear interrupt */
+ IO_INTC_IRQ2 = (1<<3);
+
+ timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0);
+}
+#endif
+
+bool sd_removable(IF_MD_NONVOID(int card_no))
+{
+#ifndef HAVE_MULTIDRIVE
+ const int card_no = 0;
+#endif
+
+ return (card_no == CARD_NUM_SLOT);
+}
+
+bool sd_present(IF_MD_NONVOID(int card_no))
+{
+#ifndef HAVE_MULTIDRIVE
+ const int card_no = 0;
+#endif
+
+ return (card_no == CARD_NUM_SLOT) ? card_detect_target() :
+#ifdef SANSA_CONNECT
+ true; /* iNAND is always present */
+#else
+ false;
+#endif
+}
+
+#else /* no hotswap */
+
+bool sd_removable(IF_MD_NONVOID(int card_no))
+{
+#ifdef HAVE_MULTIDRIVE
+ (void)card_no;
+#endif
+
+ /* not applicable */
+ return false;
+}
+
+#endif /* HAVE_HOTSWAP */
+
+static void sd_thread(void) NORETURN_ATTR;
+static void sd_thread(void)
+{
+ struct queue_event ev;
+
+ /* TODO */
+ while (1)
+ {
+ queue_wait_w_tmo(&sd_queue, &ev, HZ);
+ switch ( ev.id )
+ {
+#ifdef HAVE_HOTSWAP
+ case SYS_HOTSWAP_INSERTED:
+ case SYS_HOTSWAP_EXTRACTED:
+ {
+ int success = 1;
+ 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(0); /* 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[0].initialized = 0;
+
+ if (ev.id == SYS_HOTSWAP_INSERTED)
+ {
+ /* FIXME: once sd_enabled is implement properly,
+ * reinitializing the controllers might be needed */
+ sd_enable(true);
+ if (success < 0) /* initialisation failed */
+ panicf("SD init failed : %d", success);
+ success = disk_mount(0); /* 0 if fail */
+ }
+
+ /* notify the system about the changed filesystems
+ */
+ if (success)
+ queue_broadcast(SYS_FS_CHANGED, 0);
+
+ /* Access is now safe */
+ mutex_unlock(&sd_mtx);
+ fat_unlock();
+ sd_enable(false);
+ }
+ break;
+#endif
+ }
+ }
+}
+
+static int sd_wait_for_state(unsigned int state)
+{
+ unsigned long response = 0;
+ unsigned int timeout = HZ; /* ticks */
+ long t = current_tick;
+
+ while (1)
+ {
+ long tick;
+ int ret = sd_command(SD_SEND_STATUS, currcard->rca,
+ SDHC_RESP_FMT_1, &response);
+ if (ret < 0)
+ return ret;
+
+ if ((SD_R1_CURRENT_STATE(response) == state))
+ {
+ return EC_OK;
+ }
+
+ if(TIME_AFTER(current_tick, t + timeout))
+ return -2;
+
+ if (TIME_AFTER((tick = current_tick), next_yield))
+ {
+ yield();
+ timeout += current_tick - tick;
+ next_yield = tick + MIN_YIELD_PERIOD;
+ }
+ }
+}
+
+static int sd_transfer_sectors(int card_no, unsigned long start,
+ int count, void *buffer, bool write)
+{
+ int ret;
+ unsigned long start_addr;
+ int dma_channel = -1;
+ bool use_direct_dma;
+ int count_per_dma;
+ unsigned long rel_addr;
+
+ dbgprintf("transfer %d %d %d", card_no, start, count);
+ mutex_lock(&sd_mtx);
+ sd_enable(true);
+
+sd_transfer_retry:
+ if (card_no == CARD_NUM_SLOT && !card_detect_target())
+ {
+ /* no external sd-card inserted */
+ ret = -EC_NOCARD;
+ goto sd_transfer_error;
+ }
+
+ sd_select_device(card_no);
+
+ if (currcard->initialized < 0)
+ {
+ ret = currcard->initialized;
+ goto sd_transfer_error;
+ }
+
+ last_disk_activity = current_tick;
+
+ ret = sd_wait_for_state(SD_TRAN);
+ if (ret < EC_OK)
+ {
+ goto sd_transfer_error;
+ }
+
+ IO_MMC_BLOCK_LENGTH = currcard->blocksize;
+
+ start_addr = start;
+
+ do
+ {
+ count_per_dma = count;
+
+ if (((unsigned long)buffer) & 0x1F)
+ {
+ /* MMC/SD interface requires 32-byte alignment of buffer */
+ use_direct_dma = false;
+ if (count > UNALIGNED_NUM_SECTORS)
+ {
+ count_per_dma = UNALIGNED_NUM_SECTORS;
+ }
+ }
+ else
+ {
+ use_direct_dma = true;
+ }
+
+ if (write == true)
+ {
+ if (use_direct_dma == false)
+ {
+ memcpy(aligned_buffer, buffer, count_per_dma*SD_BLOCK_SIZE);
+ }
+ commit_dcache_range(use_direct_dma ? buffer : aligned_buffer,
+ count_per_dma*SD_BLOCK_SIZE);
+ }
+
+ IO_MMC_NR_BLOCKS = count_per_dma;
+
+ /* Set start_addr to the correct unit (blocks or bytes) */
+ if (!(card_info[card_no].ocr & SD_OCR_CARD_CAPACITY_STATUS))
+ start_addr *= SD_BLOCK_SIZE; /* not SDHC */
+
+ ret = sd_command(write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK,
+ start_addr, MMC_CMD_DCLR | MMC_CMD_DATA |
+ SDHC_RESP_FMT_1 | (write ? MMC_CMD_WRITE : 0),
+ NULL);
+
+ if (ret < 0)
+ goto sd_transfer_error;
+
+ /* other burst modes are not supported for this peripheral */
+ dma_channel = dma_request_channel(DMA_PERIPHERAL_MMCSD,
+ DMA_MODE_8_BURST);
+
+ if (use_direct_dma == true)
+ {
+ rel_addr = ((unsigned long)buffer)-CONFIG_SDRAM_START;
+ }
+ else
+ {
+ rel_addr = ((unsigned long)aligned_buffer)-CONFIG_SDRAM_START;
+ }
+
+ IO_MMC_SD_DMA_ADDR_LOW = rel_addr & 0xFFFF;
+ IO_MMC_SD_DMA_ADDR_HI = (rel_addr & 0xFFFF0000) >> 16;
+
+ IO_MMC_SD_DMA_MODE |= MMC_DMAMODE_ENABLE;
+ if (write == true)
+ {
+ IO_MMC_SD_DMA_MODE |= MMC_DMAMODE_WRITE;
+ }
+
+ IO_MMC_SD_DMA_TRIGGER = 1;
+
+ dbgprintf("SD DMA transfer in progress");
+
+ ret = dma_wait_for_completion();
+ dma_release_channel(dma_channel);
+
+ dbgprintf("SD DMA transfer complete");
+
+ if (ret != EC_OK)
+ {
+ goto sd_transfer_error;
+ }
+
+ count -= count_per_dma;
+
+ if (write == false)
+ {
+ discard_dcache_range(use_direct_dma ? buffer : aligned_buffer,
+ count_per_dma*SD_BLOCK_SIZE);
+
+ if (use_direct_dma == false)
+ {
+ memcpy(buffer, aligned_buffer, count_per_dma*SD_BLOCK_SIZE);
+ }
+ }
+
+ buffer += count_per_dma*SD_BLOCK_SIZE;
+ start_addr += count_per_dma;
+
+ last_disk_activity = current_tick;
+
+ ret = sd_command(SD_STOP_TRANSMISSION, 0, SDHC_RESP_FMT_1, NULL);
+ if (ret < 0)
+ {
+ goto sd_transfer_error;
+ }
+
+ ret = sd_wait_for_state(SD_TRAN);
+ if (ret < 0)
+ {
+ goto sd_transfer_error;
+ }
+ } while (count > 0);
+
+ while (1)
+ {
+ sd_enable(false);
+ mutex_unlock(&sd_mtx);
+
+ return ret;
+
+sd_transfer_error:
+ if (sd_status[card_no].retry < sd_status[card_no].retry_max
+ && ret != -EC_NOCARD)
+ {
+ sd_status[card_no].retry++;
+ currcard->initialized = 0;
+ goto sd_transfer_retry;
+ }
+ }
+}
+
+int sd_read_sectors(IF_MD2(int card_no,) unsigned long start, int incount,
+ void* inbuf)
+{
+#ifndef HAVE_MULTIDRIVE
+ const int card_no = 0;
+#endif
+ return sd_transfer_sectors(card_no, start, incount, inbuf, false);
+}
+
+int sd_write_sectors(IF_MD2(int card_no,) unsigned long start, int count,
+ const void* outbuf)
+{
+#ifndef BOOTLOADER
+#ifndef HAVE_MULTIDRIVE
+ const int card_no = 0;
+#endif
+ return sd_transfer_sectors(card_no, start, count, (void*)outbuf, true);
+#else /* we don't need write support in bootloader */
+#ifdef HAVE_MULTIDRIVE
+ (void)card_no;
+#endif
+ (void)start;
+ (void)count;
+ (void)outbuf;
+ return 0;
+#endif
+}
+
+int sd_init(void)
+{
+ int ret = EC_OK;
+
+#ifndef BOOTLOADER
+ sd_enabled = true;
+ sd_enable(false);
+#endif
+ mutex_init(&sd_mtx);
+
+ mutex_lock(&sd_mtx);
+ initialized = true;
+
+ /* based on linux/drivers/mmc/dm320mmc.c
+ Copyright (C) 2006 ZSI, All Rights Reserved.
+ Written by: Ben Bostwick */
+
+ bitclr16(&IO_CLK_MOD2, CLK_MOD2_MMC);
+ bitset16(&IO_CLK_INV, CLK_INV_MMC);
+
+ /* mmc module clock: 75 Mhz (AHB) / 2 = ~37.5 Mhz */
+ /* OF uses 1, but for some reason it freezes on us */
+ IO_CLK_DIV3 = (IO_CLK_DIV3 & 0xFF00) | 0x02;
+
+ bitset16(&IO_CLK_MOD2, CLK_MOD2_MMC);
+
+ /* set mmc module into reset */
+ bitset16(&IO_MMC_CONTROL, (MMC_CTRL_DATRST | MMC_CTRL_CMDRST));
+
+ /* set resp timeout to max */
+ IO_MMC_RESPONSE_TIMEOUT |= 0x1FFF;
+ IO_MMC_READ_TIMEOUT = 0xFFFF;
+
+ /* all done, take mmc module out of reset */
+ bitclr16(&IO_MMC_CONTROL, (MMC_CTRL_DATRST | MMC_CTRL_CMDRST));
+
+#ifdef SANSA_CONNECT
+ /* GIO37 - Power Card; GIO38 - Power iNAND (both active low) */
+ IO_GIO_DIR2 &= ~((1 << 5) /* GIO37 */ | (1 << 6) /* GIO38 */);
+ IO_GIO_INV2 &= ~((1 << 5) /* GIO37 */ | (1 << 6) /* GIO38 */);
+ IO_GIO_BITCLR2 = (1 << 5) | (1 << 6);
+
+ /* GIO6 - select Card; GIO5 - select iNAND (both active low) */
+ IO_GIO_DIR0 &= ~((1 << 6) /* GIO6 */ | (1 << 5) /* GIO5 */);
+ IO_GIO_INV0 &= ~((1 << 6) /* GIO6 */ | (1 << 5) /* GIO5 */);
+ IO_GIO_BITSET0 = (1 << 6) | (1 << 5);
+
+#ifdef HAVE_HOTSWAP
+ /* GIO14 is card detect */
+ IO_GIO_DIR0 |= (1 << 14); /* Set GIO14 as input */
+ IO_GIO_INV0 &= ~(1 << 14); /* GIO14 not inverted */
+ IO_GIO_IRQPORT |= (1 << 14); /* Enable GIO14 external interrupt */
+ IO_GIO_IRQEDGE |= (1 << 14); /* Any edge detection */
+
+ /* Enable GIO14 interrupt */
+ IO_INTC_EINT2 |= INTR_EINT2_EXT14;
+#endif
+#endif
+
+ sd_select_device(1);
+
+ /* Enable Memory Card CLK */
+ bitset16(&IO_MMC_MEM_CLK_CONTROL, (1 << 8));
+
+ queue_init(&sd_queue, true);
+ sd_thread_id = create_thread(sd_thread, sd_stack, sizeof(sd_stack),
+ 0, sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+ IF_COP(, CPU));
+
+ mutex_unlock(&sd_mtx);
+
+ return ret;
+}
+
+long sd_last_disk_activity(void)
+{
+ return last_disk_activity;
+}
+
+tCardInfo *card_get_info_target(int card_no)
+{
+ return &card_info[card_no];
+}
+
+void sd_sleepnow(void)
+{
+}
+