778 lines
22 KiB
C
778 lines
22 KiB
C
|
/*
|
||
|
* (C) Copyright 2003
|
||
|
* Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
|
||
|
*
|
||
|
* See file CREDITS for list of people who contributed to this
|
||
|
* project.
|
||
|
*
|
||
|
* 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 program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||
|
* MA 02111-1307 USA
|
||
|
*/
|
||
|
//#define DEBUG
|
||
|
|
||
|
#include <config.h>
|
||
|
#include <common.h>
|
||
|
#include "mvsdmmc.h"
|
||
|
#include <mmc.h>
|
||
|
#include <asm/errno.h>
|
||
|
#include <part.h>
|
||
|
#include "mvOs.h"
|
||
|
|
||
|
#ifdef CONFIG_MMC
|
||
|
|
||
|
static int is_sdhc;
|
||
|
|
||
|
extern int
|
||
|
fat_register_device(block_dev_desc_t *dev_desc, int part_no);
|
||
|
|
||
|
static block_dev_desc_t mmc_dev;
|
||
|
|
||
|
block_dev_desc_t * mmc_get_dev(int dev)
|
||
|
{
|
||
|
return ((block_dev_desc_t *)&mmc_dev);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FIXME needs to read cid and csd info to determine block size
|
||
|
* and other parameters
|
||
|
*/
|
||
|
static uchar mmc_buf[MMC_BLOCK_SIZE];
|
||
|
static mmc_csd_t mmc_csd;
|
||
|
static int mmc_ready = 0;
|
||
|
|
||
|
/* MMC_DEFAULT_RCA should probably be just 1, but this may break other code
|
||
|
that expects it to be shifted. */
|
||
|
static u_int16_t rca = 0;
|
||
|
|
||
|
static u_int32_t mmc_size(const struct mmc_csd *csd)
|
||
|
{
|
||
|
u_int32_t block_len, mult, blocknr;
|
||
|
|
||
|
block_len = csd->read_bl_len << 12;
|
||
|
mult = csd->c_size_mult1 << 8;
|
||
|
blocknr = (csd->c_size+1) * mult;
|
||
|
|
||
|
return blocknr * block_len;
|
||
|
}
|
||
|
|
||
|
static int isprint (unsigned char ch)
|
||
|
{
|
||
|
if (ch >= 32 && ch < 127)
|
||
|
return (1);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int toprint(char *dst, char c)
|
||
|
{
|
||
|
if (isprint(c)) {
|
||
|
*dst = c;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return sprintf(dst,"\\x%02x", c);
|
||
|
|
||
|
}
|
||
|
|
||
|
static void print_mmc_cid(mmc_cid_t *cid)
|
||
|
{
|
||
|
printf("MMC found. Card desciption is:\n");
|
||
|
printf("Manufacturer ID = %02x%02x%02x\n",
|
||
|
cid->id[0], cid->id[1], cid->id[2]);
|
||
|
printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev);
|
||
|
cid->hwrev = cid->fwrev = 0; /* null terminate string */
|
||
|
printf("Product Name = %s\n",cid->name);
|
||
|
printf("Serial Number = %02x%02x%02x\n",
|
||
|
cid->sn[0], cid->sn[1], cid->sn[2]);
|
||
|
printf("Month = %d\n",cid->month);
|
||
|
printf("Year = %d\n",1997 + cid->year);
|
||
|
}
|
||
|
|
||
|
static void print_sd_cid(sd_cid_t *cid)
|
||
|
{
|
||
|
int len;
|
||
|
char tbuf[64];
|
||
|
|
||
|
printf("SD%s found. Card desciption is:\n", is_sdhc?"HC":"");
|
||
|
|
||
|
len = 0;
|
||
|
len += toprint(&tbuf[len], cid->oid_0);
|
||
|
len += toprint(&tbuf[len], cid->oid_1);
|
||
|
tbuf[len] = 0;
|
||
|
|
||
|
printf("Manufacturer: 0x%02x, OEM \"%s\"\n",
|
||
|
cid->mid, tbuf);
|
||
|
|
||
|
len = 0;
|
||
|
len += toprint(&tbuf[len], cid->pnm_0);
|
||
|
len += toprint(&tbuf[len], cid->pnm_1);
|
||
|
len += toprint(&tbuf[len], cid->pnm_2);
|
||
|
len += toprint(&tbuf[len], cid->pnm_3);
|
||
|
len += toprint(&tbuf[len], cid->pnm_4);
|
||
|
tbuf[len] = 0;
|
||
|
|
||
|
printf("Product name: \"%s\", revision %d.%d\n",
|
||
|
tbuf,
|
||
|
cid->prv >> 4, cid->prv & 15);
|
||
|
|
||
|
printf("Serial number: %u\n",
|
||
|
cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 |
|
||
|
cid->psn_3);
|
||
|
printf("Manufacturing date: %d/%d\n",
|
||
|
cid->mdt_1 & 15,
|
||
|
2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4));
|
||
|
|
||
|
printf("CRC: 0x%02x, b0 = %d\n",
|
||
|
cid->crc >> 1, cid->crc & 1);
|
||
|
}
|
||
|
|
||
|
static void mvsdmmc_set_clock(unsigned int clock)
|
||
|
{
|
||
|
unsigned int m;
|
||
|
|
||
|
m = MVSDMMC_BASE_FAST_CLOCK/(2*clock) - 1;
|
||
|
|
||
|
debug("mvsdmmc_set_clock: dividor = 0x%x clock=%d\n",
|
||
|
m, clock);
|
||
|
|
||
|
SDIO_REG_WRITE32(SDIO_CLK_DIV, m & 0x7ff);
|
||
|
|
||
|
if (isprint(1))
|
||
|
udelay(10*1000);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ulong *
|
||
|
/****************************************************/
|
||
|
mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
static ulong resp[4];
|
||
|
ushort done ;
|
||
|
int err = 0 ;
|
||
|
ulong curr, start, diff, hz;
|
||
|
ushort response[8], resp_indx = 0;
|
||
|
|
||
|
debug("mmc_cmd %x, arg: %x,xfer: %x,resp: %x, wait : %x\n", cmd, arg, xfermode, resptype, waittype);
|
||
|
|
||
|
//clear status
|
||
|
SDIO_REG_WRITE16(SDIO_NOR_INTR_STATUS, 0xffff);
|
||
|
SDIO_REG_WRITE16(SDIO_ERR_INTR_STATUS, 0xffff);
|
||
|
|
||
|
start = get_ticks();
|
||
|
hz = get_tbclk();
|
||
|
|
||
|
while((SDIO_REG_READ16(SDIO_PRESENT_STATE0) & CARD_BUSY)) {
|
||
|
curr = get_ticks();
|
||
|
diff = (long) curr - (long) start;
|
||
|
if (diff > (3*hz))
|
||
|
{
|
||
|
// 3 seconds timeout, card busy, can't sent cmd
|
||
|
printf("card too busy \n");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SDIO_REG_WRITE16(SDIO_ARG_LOW, (ushort)(arg&0xffff) );
|
||
|
SDIO_REG_WRITE16(SDIO_ARG_HI, (ushort)(arg>>16) );
|
||
|
SDIO_REG_WRITE16(SDIO_XFER_MODE, xfermode);
|
||
|
if( (cmd == MMC_CMD_READ_BLOCK) || (cmd == 25) )
|
||
|
{
|
||
|
SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype | 0x3c ) );
|
||
|
debug("cmd reg : %x\n", SDIO_REG_READ16( SDIO_CMD )) ;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype ) );
|
||
|
}
|
||
|
|
||
|
done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype;
|
||
|
start = get_ticks();
|
||
|
|
||
|
while( done!=waittype)
|
||
|
{
|
||
|
done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype;
|
||
|
|
||
|
if( SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & 0x8000 )
|
||
|
{
|
||
|
printf("Error! cmd : %d, err : %04x\n", cmd, SDIO_REG_READ16(SDIO_ERR_INTR_STATUS) ) ;
|
||
|
return 0 ; // error happen
|
||
|
}
|
||
|
|
||
|
curr = get_ticks();
|
||
|
diff = (long) curr - (long) start;
|
||
|
if (diff > (3*hz))
|
||
|
{
|
||
|
printf("cmd timeout, status : %04x\n", SDIO_REG_READ16(SDIO_NOR_INTR_STATUS));
|
||
|
printf("xfer mode : %04x\n", SDIO_REG_READ16(SDIO_XFER_MODE));
|
||
|
err = 1 ;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (resp_indx = 0 ; resp_indx < 8; resp_indx++)
|
||
|
response[resp_indx] = SDIO_REG_READ16(SDIO_RSP(resp_indx));
|
||
|
|
||
|
memset(resp, 0, sizeof(resp));
|
||
|
|
||
|
switch (resptype & 0x3) {
|
||
|
case SDIO_CMD_RSP_48:
|
||
|
case SDIO_CMD_RSP_48BUSY:
|
||
|
resp[0] = ((response[2] & 0x3f) << (8 - 8)) |
|
||
|
((response[1] & 0xffff) << (14 - 8)) |
|
||
|
((response[0] & 0x3ff) << (30 - 8));
|
||
|
resp[1] = ((response[0] & 0xfc00) >> 10);
|
||
|
break;
|
||
|
|
||
|
case SDIO_CMD_RSP_136:
|
||
|
resp[3] = ((response[7] & 0x3fff) << 8) |
|
||
|
((response[6] & 0x3ff) << 22);
|
||
|
resp[2] = ((response[6] & 0xfc00) >> 10) |
|
||
|
((response[5] & 0xffff) << 6) |
|
||
|
((response[4] & 0x3ff) << 22);
|
||
|
resp[1] = ((response[4] & 0xfc00) >> 10) |
|
||
|
((response[3] & 0xffff) << 6) |
|
||
|
((response[2] & 0x3ff) << 22);
|
||
|
resp[0] = ((response[2] & 0xfc00) >> 10) |
|
||
|
((response[1] & 0xffff) << 6) |
|
||
|
((response[0] & 0x3ff) << 22);
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
#ifdef MMC_DEBUG
|
||
|
int i;
|
||
|
printf("MMC resp :");
|
||
|
for (i=0; i<4; ++i ) {
|
||
|
printf(" %08x", resp[i]);
|
||
|
}
|
||
|
printf("\n");
|
||
|
#endif
|
||
|
if( err )
|
||
|
return NULL ;
|
||
|
else
|
||
|
return resp;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
/****************************************************/
|
||
|
mmc_block_read(uchar *dst, ulong src, ulong len)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
ulong *resp;
|
||
|
//ushort argh, argl;
|
||
|
//ulong status;
|
||
|
|
||
|
if (len == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (is_sdhc) {
|
||
|
/* SDHC: use block address */
|
||
|
src >>= 9;
|
||
|
}
|
||
|
|
||
|
debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, len);
|
||
|
|
||
|
#if 0
|
||
|
/* set block len */
|
||
|
resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, len, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
if (!resp) {
|
||
|
printf("mmc_block_read: set blk len fails\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// prepare for dma transfer
|
||
|
SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(dst))&0xffff);
|
||
|
SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,(((ulong)dst)>>16)&0xffff);
|
||
|
SDIO_REG_WRITE16(SDIO_BLK_SIZE,len);
|
||
|
SDIO_REG_WRITE16(SDIO_BLK_COUNT,1);
|
||
|
|
||
|
/* send read command */
|
||
|
resp = mmc_cmd(MMC_CMD_READ_BLOCK, src, 0x10 , // 0x12,
|
||
|
SDIO_CMD_RSP_48, SDIO_NOR_XFER_DONE);
|
||
|
if (!resp) {
|
||
|
printf("mmc_block_read: mmc read block cmd fails\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
/****************************************************/
|
||
|
mmc_block_write(ulong dst, uchar *src, int len)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
//uchar *resp;
|
||
|
//ushort argh, argl;
|
||
|
//ulong status;
|
||
|
return -1 ;
|
||
|
#if 0
|
||
|
if (len == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong)src, len);
|
||
|
|
||
|
argh = len >> 16;
|
||
|
argl = len & 0xffff;
|
||
|
|
||
|
/* set block len */
|
||
|
resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
|
||
|
|
||
|
/* send write command */
|
||
|
argh = dst >> 16;
|
||
|
argl = dst & 0xffff;
|
||
|
MMC_STRPCL = MMC_STRPCL_STOP_CLK;
|
||
|
MMC_NOB = 1;
|
||
|
MMC_BLKLEN = len;
|
||
|
resp = mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl,
|
||
|
MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
|
||
|
|
||
|
MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ;
|
||
|
while (len) {
|
||
|
if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) {
|
||
|
int i, bytes = min(32,len);
|
||
|
|
||
|
for (i=0; i<bytes; i++) {
|
||
|
MMC_TXFIFO = *src++;
|
||
|
}
|
||
|
if (bytes < 32) {
|
||
|
MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL;
|
||
|
}
|
||
|
len -= bytes;
|
||
|
}
|
||
|
status = MMC_STAT;
|
||
|
if (status & MMC_STAT_ERRORS) {
|
||
|
printf("MMC_STAT error %lx\n", status);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
|
||
|
while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE));
|
||
|
MMC_I_MASK = ~MMC_I_MASK_PRG_DONE;
|
||
|
while (!(MMC_I_REG & MMC_I_REG_PRG_DONE));
|
||
|
status = MMC_STAT;
|
||
|
if (status & MMC_STAT_ERRORS) {
|
||
|
printf("MMC_STAT error %lx\n", status);
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
/****************************************************/
|
||
|
mmc_read(ulong src, uchar *dst, int size)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
|
||
|
ulong mmc_block_size, mmc_block_address;
|
||
|
|
||
|
if (size == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!mmc_ready) {
|
||
|
printf("Please initial the MMC first\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
mmc_block_size = MMC_BLOCK_SIZE;
|
||
|
mmc_block_address = ~(mmc_block_size - 1);
|
||
|
|
||
|
src -= CFG_MMC_BASE;
|
||
|
end = src + size;
|
||
|
part_start = ~mmc_block_address & src;
|
||
|
part_end = ~mmc_block_address & end;
|
||
|
aligned_start = mmc_block_address & src;
|
||
|
aligned_end = mmc_block_address & end;
|
||
|
|
||
|
/* all block aligned accesses */
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if (part_start) {
|
||
|
part_len = mmc_block_size - part_start;
|
||
|
debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
memcpy(dst, mmc_buf+part_start, part_len);
|
||
|
dst += part_len;
|
||
|
src += part_len;
|
||
|
}
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
for (; src < aligned_end; aligned_start +=mmc_block_size, src += mmc_block_size, dst += mmc_block_size) {
|
||
|
debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0) {
|
||
|
printf("mmc block read error\n");
|
||
|
return -1;
|
||
|
}
|
||
|
//printf("mem copy from %x to %x, size %d\n", (ulong)mmc_buf, (ulong)dst, mmc_block_size );
|
||
|
memcpy(dst, mmc_buf, mmc_block_size);
|
||
|
}
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if (part_end && src < end) {
|
||
|
debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
memcpy(dst, mmc_buf, part_end);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
/****************************************************/
|
||
|
mmc_write(uchar *src, ulong dst, int size)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
|
||
|
ulong mmc_block_size, mmc_block_address;
|
||
|
|
||
|
if (size == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!mmc_ready) {
|
||
|
printf("Please initial the MMC first\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
mmc_block_size = MMC_BLOCK_SIZE;
|
||
|
mmc_block_address = ~(mmc_block_size - 1);
|
||
|
|
||
|
dst -= CFG_MMC_BASE;
|
||
|
end = dst + size;
|
||
|
part_start = ~mmc_block_address & dst;
|
||
|
part_end = ~mmc_block_address & end;
|
||
|
aligned_start = mmc_block_address & dst;
|
||
|
aligned_end = mmc_block_address & end;
|
||
|
|
||
|
/* all block aligned accesses */
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if (part_start) {
|
||
|
part_len = mmc_block_size - part_start;
|
||
|
debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
(ulong)src, dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
memcpy(mmc_buf+part_start, src, part_len);
|
||
|
if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
dst += part_len;
|
||
|
src += part_len;
|
||
|
}
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
|
||
|
debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_write(dst, (uchar *)src, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if (part_end && dst < end) {
|
||
|
debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
|
||
|
src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
|
||
|
if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
memcpy(mmc_buf, src, part_end);
|
||
|
if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ulong
|
||
|
/****************************************************/
|
||
|
mmc_bread(int dev_num, ulong blknr, ulong blkcnt, ulong *dst)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
int mmc_block_size = MMC_BLOCK_SIZE;
|
||
|
ulong src = blknr * mmc_block_size + CFG_MMC_BASE;
|
||
|
|
||
|
mmc_read(src, (uchar *)dst, blkcnt*mmc_block_size);
|
||
|
return blkcnt;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
/****************************************************/
|
||
|
mmc_init(int verbose)
|
||
|
/****************************************************/
|
||
|
{
|
||
|
int retries, rc = -ENODEV;
|
||
|
ulong *resp;
|
||
|
int sd_ver20;
|
||
|
int is_sd;
|
||
|
ushort reg;
|
||
|
uchar cidbuf[64];
|
||
|
|
||
|
sd_ver20 = 0;
|
||
|
is_sdhc = 0;
|
||
|
is_sd = 0;
|
||
|
|
||
|
// Initial Host Ctrl : Timeout : max , Normal Speed mode, 4-bit data mode
|
||
|
// Big Endian, SD memory Card, Push_pull CMD Line
|
||
|
SDIO_REG_WRITE16(SDIO_HOST_CTRL,
|
||
|
SDIO_HOST_CTRL_TMOUT(0xf) |
|
||
|
SDIO_HOST_CTRL_DATA_WIDTH_4_BITS |
|
||
|
SDIO_HOST_CTRL_BIG_ENDIAN |
|
||
|
SDIO_HOST_CTRL_PUSH_PULL_EN |
|
||
|
SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY );
|
||
|
|
||
|
SDIO_REG_WRITE16(SDIO_CLK_CTRL, 0);
|
||
|
|
||
|
//enable status
|
||
|
SDIO_REG_WRITE16(SDIO_NOR_STATUS_EN, 0xffff);
|
||
|
SDIO_REG_WRITE16(SDIO_ERR_STATUS_EN, 0xffff);
|
||
|
|
||
|
//disable interrupts
|
||
|
SDIO_REG_WRITE16(SDIO_NOR_INTR_EN, 0);
|
||
|
SDIO_REG_WRITE16(SDIO_ERR_INTR_EN, 0);
|
||
|
|
||
|
SDIO_REG_WRITE16(SDIO_SW_RESET,0x100);
|
||
|
udelay(10000);
|
||
|
|
||
|
mmc_csd.c_size = 0;
|
||
|
|
||
|
/* reset */
|
||
|
retries = 10;
|
||
|
//mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype);
|
||
|
resp = mmc_cmd(0, 0, 0, SDIO_CMD_RSP_NONE , SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 0 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
debug ("trying to detect SD card version\n");
|
||
|
resp = mmc_cmd(8, 0x000001aa, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 8 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
if (resp && (resp[0] & 0x1ff)==0x1aa) {
|
||
|
debug("sd version 2.0 card detected\n");
|
||
|
sd_ver20 = 1;
|
||
|
}
|
||
|
|
||
|
if (sd_ver20)
|
||
|
retries = 50;
|
||
|
else
|
||
|
retries = 10;
|
||
|
|
||
|
while (retries--) {
|
||
|
resp = mmc_cmd(55, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 55 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
if (sd_ver20)
|
||
|
resp = mmc_cmd(41, 0x40300000, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
else
|
||
|
resp = mmc_cmd(41, 0x00300000, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
|
||
|
debug("cmd 41 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
if (resp && (resp[0] & 0x80000000)) {
|
||
|
debug ("detected SD card\n");
|
||
|
is_sd = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
udelay(100*1000);
|
||
|
}
|
||
|
|
||
|
if (retries <= 0 && !is_sd) {
|
||
|
debug ("failed to detect SD card, trying MMC\n");
|
||
|
retries = 10;
|
||
|
while (retries--) {
|
||
|
resp = mmc_cmd(1, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 01 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
if (resp && (resp[0] & 0x80000000)) {
|
||
|
debug ("detected MMC card\n");
|
||
|
reg = SDIO_REG_READ16(SDIO_HOST_CTRL);
|
||
|
reg &= ~(0x3<<1);
|
||
|
reg |= SDIO_HOST_CTRL_CARD_TYPE_IO_MMC;
|
||
|
SDIO_REG_WRITE16(SDIO_HOST_CTRL, reg);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
udelay(100*1000);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retries <= 0) {
|
||
|
debug ("detect fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
/* try to get card id */
|
||
|
resp = mmc_cmd(2, 0, 0, SDIO_CMD_RSP_136, SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 2 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
if (resp == NULL) {
|
||
|
debug ("read cid fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
if (is_sd) {
|
||
|
sd_cid_t *cid = (sd_cid_t *) resp;
|
||
|
|
||
|
memcpy(cidbuf, resp, sizeof(sd_cid_t));
|
||
|
|
||
|
sprintf((char *) mmc_dev.vendor,
|
||
|
"Man %02x OEM %c%c \"%c%c%c%c%c\"",
|
||
|
cid->mid, cid->oid_0, cid->oid_1,
|
||
|
cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3, cid->pnm_4);
|
||
|
|
||
|
sprintf((char *) mmc_dev.product, "%d",
|
||
|
(cid->psn_0 << 24) | (cid->psn_1 <<16) | (cid->psn_2 << 8) | (cid->psn_3 << 8));
|
||
|
|
||
|
sprintf((char *) mmc_dev.revision, "%d.%d", cid->prv>>4, cid->prv & 0xff);
|
||
|
|
||
|
} else {
|
||
|
/* TODO configure mmc driver depending on card attributes */
|
||
|
mmc_cid_t *cid = (mmc_cid_t *) resp;
|
||
|
|
||
|
memcpy(cidbuf, resp, sizeof(sd_cid_t));
|
||
|
|
||
|
|
||
|
sprintf((char *) mmc_dev.vendor,
|
||
|
"Man %02x%02x%02x Snr %02x%02x%02x",
|
||
|
cid->id[0], cid->id[1], cid->id[2],
|
||
|
cid->sn[0], cid->sn[1], cid->sn[2]);
|
||
|
sprintf((char *) mmc_dev.product, "%s", cid->name);
|
||
|
sprintf((char *) mmc_dev.revision, "%x %x", cid->hwrev, cid->fwrev);
|
||
|
}
|
||
|
|
||
|
/* fill in device description */
|
||
|
mmc_dev.if_type = IF_TYPE_MMC;
|
||
|
mmc_dev.part_type = PART_TYPE_DOS;
|
||
|
mmc_dev.dev = 0;
|
||
|
mmc_dev.lun = 0;
|
||
|
mmc_dev.type = 0;
|
||
|
|
||
|
/* FIXME fill in the correct size (is set to 128MByte) */
|
||
|
mmc_dev.blksz = 512;
|
||
|
mmc_dev.lba = 0x10000;
|
||
|
|
||
|
mmc_dev.removable = 0;
|
||
|
mmc_dev.block_read = mmc_bread;
|
||
|
|
||
|
/* MMC exists, get CSD too */
|
||
|
resp = mmc_cmd(MMC_CMD_SET_RCA, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
if (resp == NULL) {
|
||
|
debug ("set rca fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
debug("cmd3 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]);
|
||
|
|
||
|
if (is_sd)
|
||
|
rca = resp[0] >> 16;
|
||
|
else
|
||
|
rca = 0;
|
||
|
|
||
|
resp = mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, 0, SDIO_CMD_RSP_136,SDIO_NOR_CMD_DONE );
|
||
|
debug("cmd 9 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
if (resp == NULL) {
|
||
|
debug ("read csd fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
memcpy(&mmc_csd, (mmc_csd_t *) resp, sizeof(mmc_csd_t));
|
||
|
rc = 0;
|
||
|
mmc_ready = 1;
|
||
|
|
||
|
/* FIXME add verbose printout for csd */
|
||
|
debug ("size = %u\n", mmc_size(&mmc_csd));
|
||
|
|
||
|
resp = mmc_cmd(7, rca<<16, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE);
|
||
|
if (resp == NULL) {
|
||
|
debug ("select card fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
debug("cmd 7 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] );
|
||
|
|
||
|
if (is_sd) {
|
||
|
resp = mmc_cmd(55, rca<<16, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
if (resp == NULL) {
|
||
|
debug ("cmd55 fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
debug("cmd55 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]);
|
||
|
|
||
|
resp = mmc_cmd(6, (rca<<16) | 0x2 , 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
if (resp == NULL) {
|
||
|
debug ("cmd55 fails\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
debug("cmd6 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]);
|
||
|
}
|
||
|
|
||
|
resp = (ulong *) &mmc_csd;
|
||
|
debug("csd: 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]);
|
||
|
|
||
|
/* check SDHC */
|
||
|
if ((resp[0]&0xf0000000)==0x40000000)
|
||
|
is_sdhc = 1;
|
||
|
|
||
|
/* set block len */
|
||
|
resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, 512, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE );
|
||
|
if (!resp) {
|
||
|
printf("mmc_block_read: set blk len fails\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
if (verbose) {
|
||
|
if (is_sd)
|
||
|
print_sd_cid((sd_cid_t *) cidbuf);
|
||
|
else
|
||
|
print_mmc_cid((mmc_cid_t *) cidbuf);
|
||
|
}
|
||
|
|
||
|
mvsdmmc_set_clock(25000000);
|
||
|
|
||
|
fat_register_device(&mmc_dev,1); /* partitions start counting with 1 */
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
mmc_ident(block_dev_desc_t *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
mmc2info(ulong addr)
|
||
|
{
|
||
|
/* FIXME hard codes to 256 MB device */
|
||
|
if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0xffffffff) {
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif /* CONFIG_MMC */
|