diff options
author | Tomasz Moń <desowin@gmail.com> | 2011-11-16 14:08:01 +0000 |
---|---|---|
committer | Tomasz Moń <desowin@gmail.com> | 2011-11-16 14:08:01 +0000 |
commit | e8a8a1be43afe63079ae48ce1a9eb3052df3b1a4 (patch) | |
tree | 084e1cdf27a339ce58e24cff8fec8c31432b52db /firmware/target/arm/tms320dm320/sdmmc-dm320.c | |
parent | 992d4eb775cac48e107e18d72783ebfb39c4234f (diff) | |
download | rockbox-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.c | 949 |
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) +{ +} + |