D2: A working read/write SD(HC) driver, based on the PP equivalent. See CowonD2Info wiki for usage notes/restrictions.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21967 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
8f5264e618
commit
3a88746780
4 changed files with 881 additions and 1 deletions
|
@ -1249,6 +1249,7 @@ target/arm/lcd-as-memframe.S
|
|||
target/arm/tcc780x/adc-tcc780x.c
|
||||
target/arm/tcc780x/system-tcc780x.c
|
||||
target/arm/tcc780x/kernel-tcc780x.c
|
||||
target/arm/tcc780x/sd-tcc780x.c
|
||||
target/arm/tcc780x/cowond2/button-cowond2.c
|
||||
target/arm/tcc780x/cowond2/lcd-cowond2.c
|
||||
target/arm/tcc780x/cowond2/power-cowond2.c
|
||||
|
|
|
@ -62,7 +62,14 @@
|
|||
/* define this if you have a flash memory storage */
|
||||
#define HAVE_FLASH_STORAGE
|
||||
|
||||
#ifndef SIMULATOR
|
||||
#define CONFIG_STORAGE (STORAGE_NAND | STORAGE_SD)
|
||||
#define HAVE_MULTIDRIVE
|
||||
#define HAVE_HOTSWAP
|
||||
#define NUM_DRIVES 2
|
||||
#else
|
||||
#define CONFIG_STORAGE STORAGE_NAND
|
||||
#endif
|
||||
|
||||
#define CONFIG_NAND NAND_TCC
|
||||
|
||||
|
@ -150,6 +157,8 @@
|
|||
/* Define this if you have adjustable CPU frequency */
|
||||
#define HAVE_ADJUSTABLE_CPU_FREQ
|
||||
|
||||
#define INCLUDE_TIMEOUT_API
|
||||
|
||||
/* Offset ( in the firmware file's header ) to the file CRC */
|
||||
#define FIRMWARE_OFFSET_FILE_CRC 0
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
#define BCLKCTR (*(volatile unsigned long *)0xF3000018)
|
||||
#define SWRESET (*(volatile unsigned long *)0xF300001C)
|
||||
#define PCLKCFG0 (*(volatile unsigned long *)0xF3000020)
|
||||
#define PCLKCFG1 (*(volatile unsigned long *)0xF3000024)
|
||||
#define PCLK_SDMMC (*(volatile unsigned long *)0xF3000024)
|
||||
#define PCLKCFG2 (*(volatile unsigned long *)0xF3000028)
|
||||
#define PCLKCFG3 (*(volatile unsigned long *)0xF300002C)
|
||||
#define PCLK_LCD (*(volatile unsigned long *)0xF3000030)
|
||||
|
@ -114,6 +114,7 @@
|
|||
#define MIRQ (*(volatile unsigned long *)0xF3001028)
|
||||
#define MFIQ (*(volatile unsigned long *)0xF300102C)
|
||||
#define TMODE (*(volatile unsigned long *)0xF3001030)
|
||||
#define TMODEA (*(volatile unsigned long *)0xF300103C)
|
||||
#define ALLMASK (*(volatile unsigned long *)0xF3001044)
|
||||
#define VAIRQ (*(volatile unsigned long *)0xF3001080)
|
||||
#define VAFIQ (*(volatile unsigned long *)0xF3001084)
|
||||
|
@ -123,6 +124,7 @@
|
|||
|
||||
#define IRQ_PRIORITY_TABLE ((volatile unsigned int *)0xF30010A0)
|
||||
|
||||
#define EXT0_IRQ_MASK (1<<0)
|
||||
#define EXT3_IRQ_MASK (1<<3)
|
||||
#define TIMER0_IRQ_MASK (1<<6)
|
||||
#define DAI_RX_IRQ_MASK (1<<14)
|
||||
|
@ -243,6 +245,39 @@
|
|||
#define ECC_ERRDATA (*(volatile unsigned long *)0xF005B060)
|
||||
#define ECC_ERR (*(volatile unsigned long *)0xF005B070)
|
||||
|
||||
/* SD/MMC Controller */
|
||||
|
||||
#define SDICLK (*(volatile unsigned long *)0xF0058004)
|
||||
#define SDIARGU (*(volatile unsigned long *)0xF0058008)
|
||||
#define SDICMD (*(volatile unsigned long *)0xF005800C)
|
||||
#define SDIRSPCMD (*(volatile unsigned long *)0xF0058010)
|
||||
#define SDIRSPARGU0 (*(volatile unsigned long *)0xF0058014)
|
||||
#define SDIRSPARGU1 (*(volatile unsigned long *)0xF0058018)
|
||||
#define SDIRSPARGU2 (*(volatile unsigned long *)0xF005801C)
|
||||
#define SDIRSPARGU3 (*(volatile unsigned long *)0xF0058020)
|
||||
#define SDIDTIMER (*(volatile unsigned long *)0xF0058024)
|
||||
#define SDIDCTRL2 (*(volatile unsigned long *)0xF0058028)
|
||||
#define SDIDCTRL (*(volatile unsigned long *)0xF005802C)
|
||||
#define SDISTATUS (*(volatile unsigned long *)0xF0058030)
|
||||
#define SDIIFLAG (*(volatile unsigned long *)0xF0058034)
|
||||
#define SDIWDATA (*(volatile unsigned long *)0xF0058038)
|
||||
#define SDIRDATA (*(volatile unsigned long *)0xF005803C)
|
||||
#define SDIIENABLE (*(volatile unsigned long *)0xF0058040)
|
||||
|
||||
#define SDICMD_RES_TYPE1 1
|
||||
#define SDICMD_RES_TYPE1b 2
|
||||
#define SDICMD_RES_TYPE2 3
|
||||
#define SDICMD_RES_TYPE3 4
|
||||
#define SDICMD_RES_TYPE4 5
|
||||
#define SDICMD_RES_TYPE5 6
|
||||
#define SDICMD_RES_TYPE6 7
|
||||
|
||||
#define SDISTATUS_RESP_RCVD (1<<7)
|
||||
#define SDISTATUS_FIFO_LOAD_REQ (1<<17)
|
||||
#define SDISTATUS_FIFO_FETCH_REQ (1<<18)
|
||||
#define SDISTATUS_CMD_PATH_RDY (1<<21)
|
||||
#define SDISTATUS_MULTIBLOCK_END (1<<25)
|
||||
|
||||
/* USB 2.0 device system MMR base address */
|
||||
#define USB_BASE 0xf0010000
|
||||
|
||||
|
|
835
firmware/target/arm/tcc780x/sd-tcc780x.c
Normal file
835
firmware/target/arm/tcc780x/sd-tcc780x.c
Normal file
|
@ -0,0 +1,835 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Daniel Ankers
|
||||
* Copyright (C) 2009 Rob Purchase
|
||||
*
|
||||
* 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 "hotswap.h"
|
||||
#include "storage.h"
|
||||
#include "led.h"
|
||||
#include "thread.h"
|
||||
#include "disk.h"
|
||||
#include "fat.h"
|
||||
#include "ata_idle_notify.h"
|
||||
#include "usb.h"
|
||||
|
||||
#if defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
|
||||
#define CARD_NUM_INTERNAL 0
|
||||
#define CARD_NUM_SLOT 1
|
||||
#elif !defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
|
||||
#define CARD_NUM_SLOT 0
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
/* for compatibility */
|
||||
static long last_disk_activity = -1;
|
||||
|
||||
/** static, private data **/
|
||||
static bool initialized = false;
|
||||
|
||||
static long next_yield = 0;
|
||||
#define MIN_YIELD_PERIOD 1000
|
||||
|
||||
static tCardInfo card_info[NUM_DRIVES];
|
||||
static tCardInfo *currcard = NULL; /* current active card */
|
||||
|
||||
struct sd_card_status
|
||||
{
|
||||
int retry;
|
||||
int retry_max;
|
||||
};
|
||||
|
||||
static struct sd_card_status sd_status[NUM_DRIVES] =
|
||||
{
|
||||
#ifdef HAVE_INTERNAL_SD
|
||||
{ 0, 1 },
|
||||
#endif
|
||||
#ifdef HAVE_HOTSWAP
|
||||
{ 0, 10 }
|
||||
#endif
|
||||
};
|
||||
|
||||
/* 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 int sd_first_drive = 0;
|
||||
|
||||
|
||||
static bool sd_poll_status(unsigned int trigger, long timeout)
|
||||
{
|
||||
long t = USEC_TIMER;
|
||||
|
||||
while ((SDISTATUS & trigger) != trigger)
|
||||
{
|
||||
long time = USEC_TIMER;
|
||||
|
||||
if (TIME_AFTER(time, next_yield))
|
||||
{
|
||||
long ty = USEC_TIMER;
|
||||
yield();
|
||||
timeout += USEC_TIMER - ty;
|
||||
next_yield = ty + MIN_YIELD_PERIOD;
|
||||
}
|
||||
|
||||
if (TIME_AFTER(time, t + timeout))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sd_command(unsigned int cmd, unsigned int arg,
|
||||
unsigned long* response, unsigned int resp_type)
|
||||
{
|
||||
int sdi_cmd = cmd;
|
||||
|
||||
sdi_cmd |= (127<<12) | (1<<11); /* max wait time | enable */
|
||||
|
||||
if (resp_type)
|
||||
{
|
||||
/* response type & response required flag */
|
||||
sdi_cmd |= (resp_type<<7) | (1<<6);
|
||||
}
|
||||
|
||||
if (cmd == SD_READ_SINGLE_BLOCK ||
|
||||
cmd == SD_READ_MULTIPLE_BLOCK ||
|
||||
cmd == SD_WRITE_BLOCK ||
|
||||
cmd == SD_WRITE_MULTIPLE_BLOCK)
|
||||
{
|
||||
sdi_cmd |= (1<<10); /* request data transfer */
|
||||
}
|
||||
|
||||
if (!sd_poll_status(SDISTATUS_CMD_PATH_RDY, 100000))
|
||||
return -EC_COMMAND;
|
||||
|
||||
SDIARGU = arg;
|
||||
SDICMD = sdi_cmd;
|
||||
|
||||
udelay(10);
|
||||
|
||||
if (response == NULL)
|
||||
return 0;
|
||||
|
||||
if (!sd_poll_status(SDISTATUS_RESP_RCVD, 100000))
|
||||
return -EC_COMMAND;
|
||||
|
||||
if (resp_type == SDICMD_RES_TYPE2)
|
||||
{
|
||||
response[0] = SDIRSPARGU0;
|
||||
response[1] = SDIRSPARGU1;
|
||||
response[2] = SDIRSPARGU2;
|
||||
response[3] = SDIRSPARGU3;
|
||||
}
|
||||
else
|
||||
{
|
||||
response[0] = SDIRSPARGU0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_wait_for_state(unsigned int state, int id)
|
||||
{
|
||||
unsigned long response = 0;
|
||||
unsigned int timeout = 0x80000;
|
||||
|
||||
long start_time = USEC_TIMER;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ret = sd_command
|
||||
(SD_SEND_STATUS, currcard->rca, &response, SDICMD_RES_TYPE1);
|
||||
|
||||
long us;
|
||||
|
||||
if (ret < 0)
|
||||
return ret*100 - id;
|
||||
|
||||
if (((response >> 9) & 0xf) == state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (TIME_AFTER(USEC_TIMER, start_time + timeout))
|
||||
return -EC_WAIT_STATE_FAILED*100 - id;
|
||||
|
||||
us = USEC_TIMER;
|
||||
if (TIME_AFTER(us, next_yield))
|
||||
{
|
||||
yield();
|
||||
timeout += USEC_TIMER - us;
|
||||
next_yield = us + MIN_YIELD_PERIOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_card_mux(int card_no)
|
||||
{
|
||||
/* We only support the default card */
|
||||
(void)card_no;
|
||||
}
|
||||
|
||||
#ifdef HAVE_HOTSWAP
|
||||
|
||||
bool card_detect_target(void)
|
||||
{
|
||||
return (GPIOB & (1<<26)) == 0; /* low active */
|
||||
}
|
||||
|
||||
void card_enable_monitoring_target(bool on)
|
||||
{
|
||||
if (on)
|
||||
{
|
||||
IEN |= EXT0_IRQ_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
IEN &= ~EXT0_IRQ_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void EXT0(void)
|
||||
{
|
||||
static struct timeout sd1_oneshot;
|
||||
|
||||
timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0);
|
||||
}
|
||||
|
||||
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_info[card_no].initialized &&
|
||||
card_info[card_no].numblocks > 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool card_detect_target(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sd_removable(IF_MD_NONVOID(int card_no))
|
||||
{
|
||||
#ifndef HAVE_MULTIDRIVE
|
||||
const int card_no = 0;
|
||||
#endif
|
||||
(void)card_no;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* HAVE_HOTSWAP */
|
||||
|
||||
|
||||
static void sd_init_device(int card_no)
|
||||
{
|
||||
int ret;
|
||||
unsigned long response;
|
||||
|
||||
/* Initialise card data as blank */
|
||||
memset(currcard, 0, sizeof(*currcard));
|
||||
|
||||
/* Switch card mux to card to initialize */
|
||||
sd_card_mux(card_no);
|
||||
|
||||
#ifdef HAVE_HOTSWAP
|
||||
/* Check card is inserted */
|
||||
if (card_no == CARD_NUM_SLOT)
|
||||
{
|
||||
if (GPIOB & (1<<26))
|
||||
{
|
||||
ret = -EC_NOCARD;
|
||||
goto card_init_error;
|
||||
}
|
||||
|
||||
/* Card will not power up unless this is done */
|
||||
GPIOC_CLEAR = (1<<24);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = sd_command(SD_GO_IDLE_STATE, 0, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
/* Use slow clock during identification (24MHz / 60 = 400kHz) */
|
||||
SDICLK = (1<<12) | 59;
|
||||
|
||||
sd_command(SD_SEND_IF_COND, 0x1aa, &response, SDICMD_RES_TYPE3);
|
||||
|
||||
if (!sd_poll_status(SDISTATUS_CMD_PATH_RDY, 100000))
|
||||
goto card_init_error;
|
||||
|
||||
currcard->ocr = 0;
|
||||
|
||||
long start_tick = current_tick;
|
||||
|
||||
while ((currcard->ocr & (1<<31)) == 0
|
||||
&& TIME_BEFORE(current_tick, start_tick + HZ))
|
||||
{
|
||||
udelay(100);
|
||||
sd_command(SD_APP_CMD, 0, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
int arg = 0x100000 | ((response == 0x1aa) ? (1<<30):0);
|
||||
sd_command(SD_APP_OP_COND, arg, &currcard->ocr, SDICMD_RES_TYPE3);
|
||||
}
|
||||
|
||||
if ((currcard->ocr & (1<<31)) == 0)
|
||||
{
|
||||
ret = -EC_POWER_UP;
|
||||
goto card_init_error;
|
||||
}
|
||||
|
||||
ret = sd_command
|
||||
(SD_ALL_SEND_CID, 0, currcard->cid, SDICMD_RES_TYPE2);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
ret = sd_command
|
||||
(SD_SEND_RELATIVE_ADDR, 0, &currcard->rca, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
ret = sd_command
|
||||
(SD_SEND_CSD, currcard->rca, currcard->csd, SDICMD_RES_TYPE2);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
sd_parse_csd(currcard);
|
||||
|
||||
ret = sd_command
|
||||
(SD_SELECT_CARD, currcard->rca, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
ret = sd_command
|
||||
(SD_APP_CMD, currcard->rca, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
ret = sd_command /* 4 bit */
|
||||
(SD_SET_BUS_WIDTH, currcard->rca | 2, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
ret = sd_command
|
||||
(SD_SET_BLOCKLEN, currcard->blocksize, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto card_init_error;
|
||||
|
||||
currcard->initialized = 1;
|
||||
return;
|
||||
|
||||
/* Card failed to initialize so disable it */
|
||||
card_init_error:
|
||||
currcard->initialized = ret;
|
||||
return;
|
||||
}
|
||||
|
||||
/* lock must already be acquired */
|
||||
static void sd_select_device(int card_no)
|
||||
{
|
||||
currcard = &card_info[card_no];
|
||||
|
||||
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_device(card_no);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
int ret = 0;
|
||||
bool aligned;
|
||||
unsigned char* buf_end;
|
||||
|
||||
mutex_lock(&sd_mtx);
|
||||
sd_enable(true);
|
||||
led(true);
|
||||
|
||||
sd_read_retry:
|
||||
if ((card_no == CARD_NUM_SLOT) && !card_detect_target())
|
||||
{
|
||||
/* no external sd-card inserted */
|
||||
ret = -EC_NOCARD;
|
||||
goto sd_read_error;
|
||||
}
|
||||
|
||||
sd_select_device(card_no);
|
||||
|
||||
if (currcard->initialized < 0)
|
||||
{
|
||||
ret = currcard->initialized;
|
||||
goto sd_read_error;
|
||||
}
|
||||
|
||||
last_disk_activity = current_tick;
|
||||
|
||||
ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_ENTRY);
|
||||
|
||||
if (ret < 0)
|
||||
goto sd_read_error;
|
||||
|
||||
/* Use full SD clock for data transfer (PCK_SDMMC) */
|
||||
SDICLK = (1<<13) | (1<<12); /* bypass divider | enable */
|
||||
|
||||
/* Block count | FIFO count | Block size (2^9) | 4-bit bus */
|
||||
SDIDCTRL = (incount << 13) | (4<<8) | (9<<4) | (1<<2);
|
||||
SDIDCTRL |= (1<<12); /* nReset */
|
||||
|
||||
SDIDCTRL2 = (1<<2); /* multi block, read */
|
||||
|
||||
if (currcard->ocr & (1<<30))
|
||||
ret = sd_command(SD_READ_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
||||
else
|
||||
ret = sd_command(SD_READ_MULTIPLE_BLOCK, start * 512, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto sd_read_error;
|
||||
|
||||
aligned = (((int)inbuf & 3) == 0);
|
||||
|
||||
buf_end = (unsigned char *)inbuf + incount * currcard->blocksize;
|
||||
|
||||
while (inbuf < (void*)buf_end)
|
||||
{
|
||||
if (!sd_poll_status(SDISTATUS_FIFO_FETCH_REQ, 100000))
|
||||
goto sd_read_error;
|
||||
|
||||
if (aligned)
|
||||
{
|
||||
unsigned int* ptr = (unsigned int*)inbuf;
|
||||
*ptr++ = SDIRDATA;
|
||||
*ptr++ = SDIRDATA;
|
||||
*ptr++ = SDIRDATA;
|
||||
*ptr = SDIRDATA;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
int tmp_buf[4];
|
||||
|
||||
tmp_buf[0] = SDIRDATA;
|
||||
tmp_buf[1] = SDIRDATA;
|
||||
tmp_buf[2] = SDIRDATA;
|
||||
tmp_buf[3] = SDIRDATA;
|
||||
|
||||
memcpy(inbuf, tmp_buf, 16);
|
||||
}
|
||||
inbuf += 16;
|
||||
}
|
||||
|
||||
ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, SDICMD_RES_TYPE1);
|
||||
if (ret < 0)
|
||||
goto sd_read_error;
|
||||
|
||||
ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_EXIT);
|
||||
if (ret < 0)
|
||||
goto sd_read_error;
|
||||
|
||||
while (1)
|
||||
{
|
||||
led(false);
|
||||
sd_enable(false);
|
||||
mutex_unlock(&sd_mtx);
|
||||
|
||||
return ret;
|
||||
|
||||
sd_read_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_read_retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sd_write_sectors(IF_MD2(int card_no,) 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
|
||||
to improve performance */
|
||||
#ifndef HAVE_MULTIDRIVE
|
||||
const int card_no = 0;
|
||||
#endif
|
||||
int ret;
|
||||
const unsigned char *buf_end;
|
||||
bool aligned;
|
||||
|
||||
mutex_lock(&sd_mtx);
|
||||
sd_enable(true);
|
||||
led(true);
|
||||
|
||||
sd_write_retry:
|
||||
if ((card_no == CARD_NUM_SLOT) && !card_detect_target())
|
||||
{
|
||||
/* no external sd-card inserted */
|
||||
ret = -EC_NOCARD;
|
||||
goto sd_write_error;
|
||||
}
|
||||
|
||||
sd_select_device(card_no);
|
||||
|
||||
if (currcard->initialized < 0)
|
||||
{
|
||||
ret = currcard->initialized;
|
||||
goto sd_write_error;
|
||||
}
|
||||
|
||||
ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_ENTRY);
|
||||
|
||||
if (ret < 0)
|
||||
goto sd_write_error;
|
||||
|
||||
/* Use full SD clock for data transfer (PCK_SDMMC) */
|
||||
SDICLK = (1<<13) | (1<<12); /* bypass divider | enable */
|
||||
|
||||
/* Block count | FIFO count | Block size (2^9) | 4-bit bus */
|
||||
SDIDCTRL = (count<<13) | (4<<8) | (9<<4) | (1<<2);
|
||||
SDIDCTRL |= (1<<12); /* nReset */
|
||||
|
||||
SDIDCTRL2 = (1<<2) | (1<<1); /* multi block, write */
|
||||
|
||||
if (currcard->ocr & (1<<30))
|
||||
ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
||||
else
|
||||
ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start * 512, NULL, SDICMD_RES_TYPE1);
|
||||
|
||||
if (ret < 0)
|
||||
goto sd_write_error;
|
||||
|
||||
aligned = (((int)outbuf & 3) == 0);
|
||||
|
||||
buf_end = (unsigned char *)outbuf + count * currcard->blocksize;
|
||||
|
||||
while (outbuf < (void*)buf_end)
|
||||
{
|
||||
if (aligned)
|
||||
{
|
||||
unsigned int* ptr = (unsigned int*)outbuf;
|
||||
SDIWDATA = *ptr++;
|
||||
SDIWDATA = *ptr++;
|
||||
SDIWDATA = *ptr++;
|
||||
SDIWDATA = *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tmp_buf[4];
|
||||
|
||||
memcpy(tmp_buf, outbuf, 16);
|
||||
|
||||
SDIWDATA = tmp_buf[0];
|
||||
SDIWDATA = tmp_buf[1];
|
||||
SDIWDATA = tmp_buf[2];
|
||||
SDIWDATA = tmp_buf[3];
|
||||
}
|
||||
outbuf += 16;
|
||||
|
||||
/* Wait for the FIFO to empty */
|
||||
if (!sd_poll_status(SDISTATUS_FIFO_LOAD_REQ, 0x80000))
|
||||
{
|
||||
ret = -EC_FIFO_WR_EMPTY;
|
||||
goto sd_write_error;
|
||||
}
|
||||
}
|
||||
|
||||
last_disk_activity = current_tick;
|
||||
|
||||
if (!sd_poll_status(SDISTATUS_MULTIBLOCK_END, 0x80000))
|
||||
{
|
||||
ret = -EC_FIFO_WR_DONE;
|
||||
goto sd_write_error;
|
||||
}
|
||||
|
||||
ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, SDICMD_RES_TYPE1);
|
||||
if (ret < 0)
|
||||
goto sd_write_error;
|
||||
|
||||
ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_EXIT);
|
||||
if (ret < 0)
|
||||
goto sd_write_error;
|
||||
|
||||
while (1)
|
||||
{
|
||||
led(false);
|
||||
sd_enable(false);
|
||||
mutex_unlock(&sd_mtx);
|
||||
|
||||
return ret;
|
||||
|
||||
sd_write_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_write_retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_thread(void) __attribute__((noreturn));
|
||||
static void sd_thread(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
bool idle_notified = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
queue_wait_w_tmo(&sd_queue, &ev, HZ);
|
||||
|
||||
switch ( ev.id )
|
||||
{
|
||||
#ifdef HAVE_HOTSWAP
|
||||
case SYS_HOTSWAP_INSERTED:
|
||||
case SYS_HOTSWAP_EXTRACTED:
|
||||
fat_lock(); /* lock-out FAT activity first -
|
||||
prevent deadlocking via disk_mount that
|
||||
would cause a reverse-order attempt with
|
||||
another thread */
|
||||
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
|
||||
into driver that bypass the fat cache */
|
||||
|
||||
/* We now have exclusive control of fat cache and ata */
|
||||
|
||||
/* Release "by force", ensure file descriptors aren't leaked and
|
||||
any busy ones are invalid if mounting */
|
||||
disk_unmount(sd_first_drive + CARD_NUM_SLOT);
|
||||
|
||||
/* 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[CARD_NUM_SLOT].initialized = 0;
|
||||
sd_status[CARD_NUM_SLOT].retry = 0;
|
||||
|
||||
if (ev.id == SYS_HOTSWAP_INSERTED)
|
||||
disk_mount(sd_first_drive + CARD_NUM_SLOT);
|
||||
|
||||
queue_broadcast(SYS_FS_CHANGED, 0);
|
||||
|
||||
/* Access is now safe */
|
||||
mutex_unlock(&sd_mtx);
|
||||
fat_unlock();
|
||||
break;
|
||||
#endif
|
||||
case SYS_TIMEOUT:
|
||||
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
|
||||
{
|
||||
idle_notified = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* never let a timer wrap confuse us */
|
||||
next_yield = USEC_TIMER;
|
||||
|
||||
if (!idle_notified)
|
||||
{
|
||||
call_storage_idle_notifys(false);
|
||||
idle_notified = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SYS_USB_CONNECTED:
|
||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||
/* Wait until the USB cable is extracted again */
|
||||
usb_wait_for_disconnect(&sd_queue);
|
||||
break;
|
||||
|
||||
case SYS_USB_DISCONNECTED:
|
||||
usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sd_enable(bool on)
|
||||
{
|
||||
if(on)
|
||||
{
|
||||
/* Enable controller & clock */
|
||||
BCLKCTR |= DEV_SDMMC;
|
||||
PCLK_SDMMC = PCK_EN | (CKSEL_PLL0<<24) | 7; /* 192/8 = 24MHz */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Disable controller & clock */
|
||||
BCLKCTR &= ~DEV_SDMMC;
|
||||
PCLK_SDMMC &= ~PCK_EN;
|
||||
}
|
||||
}
|
||||
|
||||
int sd_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!initialized)
|
||||
mutex_init(&sd_mtx);
|
||||
|
||||
mutex_lock(&sd_mtx);
|
||||
|
||||
led(false);
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
SWRESET |= DEV_SDMMC;
|
||||
SWRESET &= ~DEV_SDMMC;
|
||||
|
||||
/* Configure dual-purpose pins for SD usage */
|
||||
PORTCFG0 &= ~(3<<16);
|
||||
PORTCFG0 |= (1<<16); /* SD_D0 & SD_D1 */
|
||||
|
||||
PORTCFG2 &= ~((3<<2) | (3<<0));
|
||||
PORTCFG2 |= ((1<<2) | (1<<0)); /* SD_D2/D3/CK/CMD */
|
||||
|
||||
/* Configure card detection GPIO as input */
|
||||
GPIOB_DIR &= ~(1<<26);
|
||||
|
||||
/* Configure card power(?) GPIO as output */
|
||||
GPIOC_DIR |= (1<<24);
|
||||
|
||||
queue_init(&sd_queue, true);
|
||||
create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0,
|
||||
sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
|
||||
IF_COP(, CPU));
|
||||
|
||||
sleep(HZ/10);
|
||||
|
||||
#ifdef HAVE_HOTSWAP
|
||||
/* Configure interrupts for the card slot */
|
||||
TMODE &= ~EXT0_IRQ_MASK; /* edge-triggered */
|
||||
TMODEA |= EXT0_IRQ_MASK; /* trigger on both edges */
|
||||
#endif
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STORAGE_MULTI
|
||||
|
||||
int sd_num_drives(int first_drive)
|
||||
{
|
||||
/* Store which logical drive number(s) we have been assigned */
|
||||
sd_first_drive = first_drive;
|
||||
|
||||
#if defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
|
||||
return 2;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sd_sleepnow(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool sd_disk_is_active(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int sd_soft_reset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_spinup_time(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STORAGE_MULTI */
|
Loading…
Reference in a new issue