www.pudn.com > sd(sd_4_data_mode).rar > sd_mem.c


/* Copyright 2002, ESS Technology, Inc.				*/ 
/* SCCSID @(#)sd_mem.c	1.40 08/10/04 */ 
 
#ifdef SD_MEMORY 
#include "common.h" 
#include "ioport.h" 
#include "mvd.h" 
#include "buffer.h" 
#include "custmem.h" 
#include "memmap.h" 
#include "smfont.h" 
#include "util.h" 
#include "constvar.h" 
#include "sd_mem.h" 
#include "sysinfo.h" 
#include "mem_dev.h" 
#include "debug.h" 
 
 
/*------------------------------------------------------------------------ 
 * Debugging macros 
 *------------------------------------------------------------------------*/ 
#define CPRINTF(a)  
#define PRINTF(a)  
#define JPRINTF(a)  
 
#define SD_ENABLE_WRITE 
 
#if 0 
int bpt; 
#define BREAKPOINT(x) {bpt=x;while(bpt) VCX_service();} 
#else 
#define BREAKPOINT(x) 
#endif 
 
/*------------------------------------------------------------------------ 
 * macros 
 *------------------------------------------------------------------------*/ 
#define	USEC(x)			(cpuclk*x/10) 
#define get_bigend32(pcbuf, off) ((uchar)pcbuf[off]|((uchar)pcbuf[off+1] << 8)| \ 
				( (uchar)pcbuf[off+2] << 16)|((uchar)pcbuf[off+3] <<24)) 
 
 
/*------------------------------------------------------------------------ 
 * Public variables 
 *------------------------------------------------------------------------*/ 
SD_CINFO SD_card_info; 
SD_XMIT_STATUS SD_xmit_info; 
 
 
/*------------------------------------------------------------------------ 
 * Private variables 
 *------------------------------------------------------------------------*/ 
static SD_CARD_STATUS *SD_card_status_ptr; /* R1 reply */ 
static SD_STATUS *SD_status_ptr; 
static SD_OCR *SD_ocr_reg_ptr; 
static SD_CID *SD_cid_reg_ptr; 
static SD_CSD *SD_csd_reg_ptr; 
static ushort SD_rca_reg; /* rel card address..ID for multi cards */ 
static SD_SCR *SD_scr_reg_ptr; 
static uint SD_rdata32; 
static int sd_is_mmc; 
static int sd_io_status = 0; 
 
static SD_CMD_Q SD_acmdq; /* SD command queue or appcmd */ 
static unsigned short sd_crc16_look_up_table[256]; 
static uchar  sd_crc7_look_up_table[256]; 
 
static int sdTotalBlockNo, sdPagePerBlock, sdEraseSecSize; 
 
/* NOTE: move LUT's to rom later */ 
/* LUT for commands with responses: R1, R1b, R2, R3, R6 */ 
uchar SD_cmd2resp[64] = { 
    /* CMD0 - CMD7 */ 
    SD_NR, SD_R3, SD_R2, SD_R6, SD_NR, SD_NR, SD_NR, SD_R1b, 
    /* CMD8 - CMD15 */ 
    SD_NR, SD_R2, SD_R2, SD_R1_RD, SD_R1b,SD_R1, SD_NR, SD_NR, 
    /* CMD16 - CMD23 */ 
    SD_R1, SD_R1_RD, SD_R1_RD, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1, 
    /* CMD24 - CMD31 */ 
    SD_R1_WR, SD_R1_WR, SD_NR, SD_R1, SD_R1b,SD_R1b,SD_R1, SD_NR, 
    /* CMD32 - CMD39 */ 
    SD_R1, SD_R1, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1b,SD_NR, 
    /* CMD40 - CMD47 */ 
    SD_NR, SD_NR, SD_R1, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, 
    /* CMD48 - CMD55 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1, 
    /* CMD56 - CMD63 */ 
    SD_R1, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR 
}; 
 
/* LUT for app. commands with responses: R1, R1b, R2, R3, R6 */ 
uchar SD_acmd2resp[52] = { 
    /* ACMD0 - ACMD7 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1, SD_NR, 
    /* ACMD8 - ACMD15 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1_RD, SD_NR, SD_NR, 
    /* ACMD16 - ACMD23 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_R1, SD_R1, 
    /* ACMD24 - ACMD31 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, 
    /* ACMD32 - ACMD8 */ 
    SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, 
    /* ACMD40 - ACMD47 */ 
    SD_NR, SD_R3, SD_R1, SD_NR, SD_NR, SD_NR, SD_NR, SD_NR, 
    /* ACMD48 - ACMD51 */ 
    SD_NR, SD_NR, SD_NR, SD_R1_RD 
}; 
 
/* LUT for SD command classes */ 
uchar SD_cmd2class[64] = { 
    /* CMD0 - CMD7 */ 
    SD_CL0,SD_CL0,SD_CL0,SD_CL0,SD_CL0,SD_CL0,SD_CL0,SD_CL0, 
    /* CMD8 - CMD15 */ 
    SD_CL0,SD_CL0,SD_CL0,SD_CL1,SD_CL0,SD_CL0,SD_CL0,SD_CL0, 
    /* CMD16 - CMD23 */ 
    SD_CL2,SD_CL2,SD_CL2,SD_CL2,SD_CL2,SD_CL2,SD_CL2,SD_CL2, 
    /* CMD24 - CMD31 */ 
    SD_CL4,SD_CL4,SD_CL4,SD_CL4,SD_CL6,SD_CL6,SD_CL6,SD_CL6, 
    /* CMD32 - CMD39 */ 
    SD_CL5,SD_CL5,SD_CL5,SD_CL5,SD_CL5,SD_CL5,SD_CL5,SD_CL5, 
    /* CMD40 - CMD47 */ 
    SD_CL5,SD_CL5,SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL7, 
    /* CMD48 - CMD55 */ 
    SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL7,SD_CL8, 
    /* CMD56 - CMD63 */ 
    SD_CL8,SD_CL8,SD_CL8,SD_CL8,SD_CL8,SD_CL8,SD_CL8,SD_CL8, 
}; 
 
/* LUT for CSD's TAAC time value */ 
uchar SD_taac_valx10[16] = { 
    00, 10, 12, 13, 15, 20, 25, 30, 
    35, 40, 45, 50, 55, 60, 70, 80 
}; 
 
#ifdef BANK2SPEEDUP 
static unsigned old_wait_state; 
#endif 
 
/*------------------------------------------------------------------------ 
 * Private functions 
 *------------------------------------------------------------------------*/ 
static int SD_set_cmd(int, uint); 
static void SD_update_card_info(void); 
static ushort SD_calc_crc16(uchar *, ushort); 
static void SD_calc_crc16_table(ushort *); 
static uchar SD_calc_crc7(uchar *, ushort); 
static void SD_calc_crc7_table(uchar *); 
static int  SD_write_blk(int, int, int, int *, int); 
 
/*------------------------------------------------------------------------ 
 * Begin SD function listing 
 *------------------------------------------------------------------------*/ 
 
/*  
  Function: Get partition boot record (PBR) address from master 
	    boot record area. 
 
  input: 
 
  output: PBR start address as physical lba (physical sector number). 
 
  side-effects: 
*/ 
unsigned int SD_boot_setup(void) 
{ 
   uchar *buff; 
   uint PBR_start_psn; 
 
    
   SD_enable_io(1); /* enable SD eaux pins */ 
 
   buff = (uchar *)dram_cached(SD_DATA_START); 
   SD_read_sector_lba((short *)buff, 0, 1, 1); /* 512B per block */ 
   if (((buff[0x0] == 0xEB) && (buff[0x2] == 0x90)) 
         || (buff[0x0] == 0xE9)) { 
        PBR_start_psn = 0; 
   }else 
       PBR_start_psn = get_bigend32(buff, 454); 
 
   return(PBR_start_psn); 
} 
/********************************************************************* 
    return: 
          1: MMC 
          0: SD or else 
**********************************************************************/ 
int SD_is_mmc(void){ 
    if(SD_card_info.type == SD_MMC) 
	return 1; 
    else 
	return 0;     
} 
/*  
  Function: Reads from specified lba to designated buffer. 
 
  input: 
	buff: pointer for SD data destination. 
	lba: physical logical block address. 
	nblk: number of blocks to read. 
 
  output: number of bytes read. 
 
  side-effects: 
*/ 
int SD_read_sector_lba(short *buff, int lba, int nblk, int immed) 
{ 
    int i, start_lba = lba; 
    short *pDest = buff; 
 
    set_bank2_wait (); 
 
    if (nblk == 1) { 
	SD_read_blk(lba<<9, 9, (unsigned int *)buff, immed); 
    } else { 
	PRINTF(("SD_multi_block read: %d-->%x : nblk %d \n", 
		 lba, buff, nblk)); 
	if(SD_card_info.type == SD_MMC){ 
	    for(i= 0; i1 && immed) 
	    status = SD_send_cmd(SD_CMD_STOP_TRANSMIT, 0, 1);  
    } 
 
    JPRINTF(("SD_write_sector_lba: data_cnt%d nblk %d, status %x\n", 
	     SD_xmit_info.data_cnt, nblk, status)); 
    return (512*nblk); 
} 
 
 
/*  
  Function: The faster CRC16 calculation procedure  
	uses 256 x 16-bit Look Up Table (8bit at a time).. 
	more efficient than generating CRC 1bit at a time. 
	(adapted from memstick.c version) 
 
  input:  
	data: pointer to data to be protected by CRC. 
	count: total bytes of data. 
 
  output: CRC16 
 
  side-effects: 
*/ 
static ushort SD_calc_crc16(uchar *data, ushort count) 
{ 
    ushort fcs = 0x0; /* initial FCS value */ 
    ushort i, t, tl, th; 
    for (i=0; i>8) ^ *data++]; 
        fcs = fcs ^ (t<<8); 
    } 
    return(fcs); 
} 
/* in case of 4-bit mode */ 
static ushort SD_calc_crc16_4_bit(int *data, ushort count, ushort *crc) 
{ 
    ushort fcs0 = 0x0, fcs1=0, fcs2=0, fcs3=0; /* initial FCS value */ 
    ushort i, j ; 
    int tmp, *pInt; 
    uint filter; 
    ushort d0,d1,d2,d3; 
    ushort tt0,tt1,tt2,tt3; 
 
    pInt = data; 
    for (i=0; i> 24); /* 0 */ 
        d3 |= ((tmp & 0x08000000) >> 21); /* 1 */ 
        d3 |= ((tmp & 0x00800000) >> 18); /* 2 */ 
        d3 |= ((tmp & 0x00080000) >> 15); /* 3 */ 
        d3 |= ((tmp & 0x00008000) >> 12); /* 4 */ 
        d3 |= ((tmp & 0x00000800) >>  9); /* 5 */ 
        d3 |= ((tmp & 0x00000080) >>  6); /* 6 */ 
        d3 |= ((tmp & 0x00000008) >>  3);       /* 7 */ 
        d2  = ((tmp & 0x40000000) >> 23); /* 0 */ 
        d2 |= ((tmp & 0x04000000) >> 20); /* 1 */ 
        d2 |= ((tmp & 0x00400000) >> 17); /* 2 */ 
        d2 |= ((tmp & 0x00040000) >> 14); /* 3 */ 
        d2 |= ((tmp & 0x00004000) >> 11); /* 4 */ 
        d2 |= ((tmp & 0x00000400) >>  8); /* 5 */ 
        d2 |= ((tmp & 0x00000040) >>  5); /* 6 */ 
        d2 |= ((tmp & 0x00000004) >>  2); /* 7 */ 
        d1  = ((tmp & 0x20000000) >> 22); /* 0 */ 
        d1 |= ((tmp & 0x02000000) >> 19); /* 1 */ 
        d1 |= ((tmp & 0x00200000) >> 16); /* 2 */ 
        d1 |= ((tmp & 0x00020000) >> 13); /* 3 */ 
        d1 |= ((tmp & 0x00002000) >> 10); /* 4 */ 
        d1 |= ((tmp & 0x00000200) >>  7); /* 5 */ 
        d1 |= ((tmp & 0x00000020) >>  4); /* 6 */ 
        d1 |= ((tmp & 0x00000002) >>  1); /* 7 */ 
        d0  = ((tmp & 0x10000000) >> 21); /* 0 */ 
        d0 |= ((tmp & 0x01000000) >> 18); /* 1 */ 
        d0 |= ((tmp & 0x00100000) >> 15); /* 2 */ 
        d0 |= ((tmp & 0x00010000) >> 12); /* 3 */ 
        d0 |= ((tmp & 0x00001000) >>  9); /* 4 */ 
        d0 |= ((tmp & 0x00000100) >>  6); /* 5 */ 
        d0 |= ((tmp & 0x00000010) >>  3); /* 6 */ 
        d0 |= ((tmp & 0x00000001) );      /* 7 */ 
 
//        PRINTF(("wanted : %x tmp %x, filter %x\n", 
//                 data_wanted, tmp, filter)); 
        tt0 = fcs0; 
        tt1 = fcs1; 
        tt2 = fcs2; 
        tt3 = fcs3; 
        fcs0 = sd_crc16_look_up_table[(tt0>>8) ^ d0]; 
        fcs1 = sd_crc16_look_up_table[(tt1>>8) ^ d1]; 
        fcs2 = sd_crc16_look_up_table[(tt2>>8) ^ d2]; 
        fcs3 = sd_crc16_look_up_table[(tt3>>8) ^ d3]; 
        fcs0 = fcs0 ^ (tt0<<8); 
        fcs1 = fcs1 ^ (tt1<<8); 
        fcs2 = fcs2 ^ (tt2<<8); 
        fcs3 = fcs3 ^ (tt3<<8); 
 
    } 
    crc[0] = fcs0; 
    crc[1] = fcs1; 
    crc[2] = fcs2; 
    crc[3] = fcs3; 
    return(0); 
} 
static uchar SD_calc_crc7(uchar *data, ushort count) 
{ 
    uchar fcs = 0x0; /* initial FCS value */ 
    ushort i; 
    uchar  g = 0x9; 
    uchar t; 
 
    for (i=0; itaac_value]; 
    data >>= 2; /* WS = 4 */ 
    cnt = SD_csd_reg_ptr->taac_unit - 5; /* n power of 10 */ 
    if (cnt < 0) { 
	do { 
	    data /= 10; 
	    cnt++; 
	} while (cnt); 
    } else { 
	while (cnt) { 
	    data *= 10; 
	    cnt--; 
	}; 
    } 
    /* read access time */ 
    SD_card_info.n_ac_max = SD_NAC_MIN + data + 
	(100 * SD_csd_reg_ptr->nsac);  
    mmc_ac = 10*(data+100*SD_csd_reg_ptr->nsac); 
    if(sd_is_mmc) 
	SD_card_info.n_ac_max  = mmc_ac; 
    JPRINTF(("n_ac_max: sd: %d, mmc %d \n", 
	     SD_card_info.n_ac_max, mmc_ac)); 
 
    /* write access time */ 
    nwrmax = (SD_card_info.n_ac_max)<<(SD_csd_reg_ptr->r2w_factor); 
    /*prevent short var n_wr_max overflow*/ 
    if (nwrmax > 0xffff) nwrmax = 0xffff; 
    SD_card_info.n_wr_max = nwrmax; 
 
    /* calculate card capacity = BLOCKNR * BLOCK_LEN */ 
    data = ((SD_csd_reg_ptr->c_size_11_2<<2) | 
	SD_csd_reg_ptr->c_size_1_0) + 1; 
    cnt = 1 << (SD_csd_reg_ptr->c_size_mult + 2); 
    data = data * cnt; /* BLOCKNR */ 
    sdTotalBlockNo = data; 
    cnt = 1 << (SD_csd_reg_ptr->rd_blk_len); /* BLOCK_LEN */ 
    sdPagePerBlock = cnt/512; 
    sdEraseSecSize = SD_csd_reg_ptr->sector_size+1; 
    SD_card_info.capacity = data * cnt; /* in Bytes */ 
} 
 
 
/*  
  Function: Initialize SD related buffers, structure members and 
	    and IO pins. 
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
void SD_init(void) 
{ 
    unsigned sclk_high, sclk_low; 
 
 
 
dawei(("SD_init()\n")); 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    /* initialization */ 
    SD_card_status_ptr = (SD_CARD_STATUS *)dram_cached(SD_CSTATUS_START); 
    SD_status_ptr = (SD_STATUS *)dram_cached(SD_STATUS_START); 
    SD_ocr_reg_ptr = (SD_OCR *)dram_cached(SD_OCR_START); 
    SD_cid_reg_ptr = (SD_CID *)dram_cached(SD_CID_START); 
    SD_csd_reg_ptr = (SD_CSD *)dram_cached(SD_CSD_START); 
    SD_scr_reg_ptr = (SD_SCR *)dram_cached(SD_SCR_START); 
 
    /* generate the crc table */  
    SD_calc_crc16_table(sd_crc16_look_up_table);  
    SD_calc_crc7_table(sd_crc7_look_up_table);  
 
    SD_rca_reg = 0; /* initially '0' */ 
    SD_card_info.type = SD_UNKNOWN; 
    sd_is_mmc = 0; 
    SD_card_info.capacity = 0; /* used to check if CSD received */ 
    SD_xmit_info.com_state = SD_IDLE; 
#if SD_CHECK_RECV_CRC 
    SD_xmit_info.crc7_err_cnt = 0; 
    SD_xmit_info.crc16_err_cnt = 0; 
#endif 
 
    SD_acmdq.cmd = SD_CMD_EMPTY; 
    SET_SD_CMD; /* initially high */ 
    SD_DETECT_INPUT;  
} 
 
 
/*  
  Function: Enable/disable SD eaux ports shared with ATAPI. This is 
	    used to prevent ATAPI initialization problems. 
 
  input: 
	enable: 1 - enable SD IO ports. 
		0 - disable SD IO ports. 
 
  output:		       
 
  side-effects: 
	(refer to sd_mem.h for specific eaux ports affected) 
*/ 
void SD_enable_io(int enable) 
{ 
#if defined(SD_MEMORY) 
    /* shared with ATAPI.. 
     * there is known conflict with ATAPI-D7 (Busy pin) that cause 
     * ATAPI initialization problem..tread lightly here. 
     */ 
    if (enable){ 
	sd_io_status = 1; 
	ENABLE_SD_GENERAL; 
	ENABLE_SD_IO; 
    } else if(sd_io_status){ 
	DISABLE_SD_GENERAL; 
	DISABLE_SD_IO; 
	sd_io_status = 0; 
    } 
#endif     
} 
 
 
/*  
  Function: SD-memory/MMC card detection. Gets relative card   
	    address(RCA) from card if detected. 
 
  input: (none) 
 
  output:		       
	-2:	failed to get CSD register 
	-1:	not voltage compatible 
	 0:	no valid SD-mem or MMC card found 
	 5:	if successful 
*/ 
int SD_identify_device(void) 
{ 
    int status, status1, cnt=0;     
 
 
    SD_enable_io(1); 
     
    SD_init(); 
 
    SD_card_info.inserted = 1; 
 
    /* Force SD card to idle-state to start initialization */ 
    SD_clk_restricted_cmd(SD_CMD_GO_IDLE_STATE, 0); 
 
    /* send ACMD41 w/acceptable OCR..  
     * and wait until SD card is ready. 
     */ 
    SD_ocr_reg_ptr->card_ready = 0; 
    do { 
	status = SD_clk_restricted_cmd(SD_CMD_APP_CMD, 0); 
	status1 = SD_clk_restricted_cmd(SD_ACMD_SD_SEND_OP_COND,  
					SD_ACCEPTABLE_OCR); 
	if ((status == -1) && (status1 == -1)) {  
	    SD_clk_restricted_cmd(SD_CMD_GO_IDLE_STATE, 0); 
	    cnt++; 
	    if (cnt > SD_MAX_RETRIES) { 
		/* timed out..no response */ 
		status = -3; 
		break; 
	    } 
	} 
    } while (!SD_ocr_reg_ptr->card_ready); 
 
    sd_is_mmc = 0; 
    /* 
     * if no response: 1) SD card not voltage compatible 
     *		       2) could be Multimedia card..verify 
     *			  by sending CMD1 (MMC's ACMD41) 
     */ 
    if (status == -3){ 
	/* check if MMC? */ 
	SD_clk_restricted_cmd(SD_CMD_GO_IDLE_STATE, 0); 
 
	cnt=0; 
	SD_ocr_reg_ptr->card_ready = 0; 
	do { 
	    status = SD_clk_restricted_cmd(SD_CMD_MMC_SEND_OP_COND,  
					   SD_ACCEPTABLE_OCR); 
	    if (status == -1) { 
		/* no response */ 
		cnt++; 
		if (cnt > SD_MAX_RETRIES){ 
		    /* card not voltage compatible */ 
		    return (-1); 
		} 
	    } 
	} while (!SD_ocr_reg_ptr->card_ready); 
 
	SD_card_info.type = SD_MMC; /* MultiMedia Card */ 
	sd_is_mmc = 1; 
    } else { 
	SD_card_info.type = SD_MEM; /* SD Memory Card */ 
    } 
 
    if(SD_card_info.type == SD_MMC){ 
	SD_rca_reg = 0x1; 
    } 
    else{ 
	SD_rca_reg = 0xffff;  
    } 
 
    /* send CMD2 to get CID number */ 
    status = SD_clk_restricted_cmd(SD_CMD_ALL_SEND_CID, 0); 
 
	 
    /* send CMD3 to get RCA */ 
    if(SD_card_info.type == SD_MMC){ 
	status = SD_clk_restricted_cmd(SD_CMD_SEND_REL_ADDR,  
				       SD_rca_reg<<16); 
    } 
    else{ 
	status = SD_clk_restricted_cmd(SD_CMD_SEND_REL_ADDR, 0); 
    } 
	 
    if (status == 1) { 
	/* send CMD9 to get Card Specific Data (CSD) */ 
	status = SD_send_cmd(SD_CMD_SEND_CSD, SD_rca_reg<<16, 1); 
	if (status == 1) { 
	    SD_update_card_info(); 
 
	    /* send card to tranfer state */ 
	    SD_send_cmd(SD_CMD_SELECT_CARD, SD_rca_reg<<16, 1);     
	    if (SD_card_info.type > SD_MMC) { 
		/* send ACMD51 to get SD configuration register */ 
		SD_scr_reg_ptr->sd_bus_widths = 0x1; /* default 1bit */ 
		SD_send_acmd(SD_ACMD_SEND_SCR, 0, 1); 
	 
		/* set to 4bit data bus if possible */ 
		SD_set_bus_width(2); 
 
		/* send ACMD13 to get SD Status register */ 
		SD_send_acmd(SD_ACMD_SD_STATUS, 0, 1); 
	    } 
	    /* we have valid SD/MMC card */ 
	    status = 5; 
	} else { 
	    status = -2; /* failed to get CSD */ 
	}    
    } else { 
	SD_card_info.type = SD_UNKNOWN; 
	status = 0; 
    }     
 
    SD_enable_io(0); 
     
    return (status); 
} 
 
 
/*  
  Function: SD-memory/MMC card reset. 
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
void SD_reset(void) 
{ 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    SD_send_cmd(SD_CMD_GO_IDLE_STATE, 0, 1); 
 
    SD_xmit_info.data_width = 0; /* initially only D0 used */ 
    SD_card_info.type = SD_UNKNOWN; 
    sd_is_mmc = 0; 
    SD_card_info.inserted = 0; 
    SD_card_info.capacity = 0; /* used to check if CSD received */ 
    SD_xmit_info.com_state = SD_IDLE; 
#if SD_CHECK_RECV_CRC 
    SD_xmit_info.crc7_err_cnt = 0; 
    SD_xmit_info.crc16_err_cnt = 0; 
#endif 
 
    SD_acmdq.cmd = SD_CMD_EMPTY; 
    SET_SD_CMD; /* initially high */ 
} 
 
 
/*  
  Function:  Set data bus width, 4bit or 1bit. 
 
  input:  
	0 - 1bit data bus (narrow bus) 
	2 - 4bit data bus (wide bus) 
 
  output:		       
	1:	success 
otherwise:	failed 
*/ 
int SD_set_bus_width(int width) 
{ 
    uint status = 0; 
     
    dawei(("SD_set_bus_width( %d )\n", width)); 
    if ((SD_scr_reg_ptr->sd_bus_widths & 0x4)) { 
	/* 4bit supported by card */ 
	if ((width>>1) != SD_xmit_info.data_width) { 
	    status = SD_send_acmd(SD_ACMD_SET_BUS_WIDTH, width, 1); 
	    if (status==1) SD_xmit_info.data_width = (width>>1); 
	} 
    } 
 
    return (status); 
} 
 
/*  
  Function: Read single block from SD. The max block size is card 
	    specific, in the range of 512 to 2048 bytes. The minimum 
	    block size is 1 byte if card support "partial block read". 
	    SD memory card always support PBR. 
	    (NOTE: currently our code is hardwired to 512B block) 
 
  input: 
	addr: location of SD data (lba). 
	blk_sz: block size to read. (2^val bytes) 
	data_ptr: SD data destination. 
	immed: 1 - complete data read in function. 
	       0 - initiate data read..let SD_service() finish 
	           in background. 
  output:		       
	-4:	unsupported block size. 
	-3:	timed out (reached retry limit) 
	-2:	communication busy 
	-1:	invalid or unsupported command or no card. 
	 0:	read initiated.."passed" to SD_service().  
     	 1:	successful 
 
  side-effects: 
*/ 
int SD_read_blk(int addr, int blk_sz, uint *data_ptr, int immed) 
{ 
    int status; 
 
 
    if (blk_sz != SD_xmit_info.cur_blk_size) { 
	/* check if card allows block size */ 
	if ((blk_sz == SD_csd_reg_ptr->rd_blk_len) || 
	    (SD_csd_reg_ptr->rd_blk_len_partial &&  
	     blk_sz < SD_csd_reg_ptr->rd_blk_len)) { 
	    SD_send_cmd(SD_CMD_SET_BLOCKLEN, (1<wr_blk_len) || 
	    (SD_csd_reg_ptr->wr_blk_partial &&  
	     blk_sz < SD_csd_reg_ptr->wr_blk_len)) { 
	    status = SD_send_cmd(SD_CMD_SET_BLOCKLEN, (1<1) 
	status=SD_send_cmd(SD_CMD_WRITE_MULTI_BLOCK, addr, immed); 
    else 
	status=SD_send_cmd(SD_CMD_WRITE_BLOCK, addr, immed); 
    if(status != 1){ 
	JPRINTF(("SD Set Write Cmd error.\n")); 
	return -1; 
    } 
    else if(SD_xmit_info.result != 0){ 
	JPRINTF(("SD Writing   error.\n")); 
	return -1; 
    } 
    return 0; 
} 
/*  
  Function: Read multiple blocks from SD. The max block size is card 
	    specific, in the range of 512 to 2048 bytes.  
	    (NOTE: currently our code is hardwired to 512B block) 
 
  input: 
	addr: location of SD data (lba). 
	nblk: number of blocks to read.  
	data_ptr: SD data destination. 
	immed: 1 - complete data read in function. 
	       0 - initiate data read..let SD_service() finish 
	           in background. 
  output:		       
	-3:	timed out (reached retry limit) 
	-2:	communication busy 
	-1:	invalid or unsupported command or no card. 
	 0:	read initiated.."passed" to SD_service().  
     	 1:	successful 
 
  side-effects: 
*/ 
int SD_read_nblk(int addr, int nblk, uint *data_ptr, int immed) 
{ 
    int status, limit; 
 
    /* make sure block size is 512B..FAT hardwired */ 
    if (SD_xmit_info.cur_blk_size != 9) { 
	SD_send_cmd(SD_CMD_SET_BLOCKLEN, 512, immed); 
    } 
    SD_xmit_info.cur_blk_size = 9; /* 512..(2^9) */ 
    SD_xmit_info.rd_dest_ptr = data_ptr; 
 
    limit = nblk*512; 
 
#if 0 
    status = SD_send_cmd(MMC_CMD_READ_UNTIL_STOP, addr, 0); 
#endif 
    status = SD_send_cmd(SD_CMD_READ_MULTI_BLOCK, addr, 0); 
    SD_xmit_info.data_len = nblk*512; 
    SD_xmit_info.data_cnt = 0; 
    /* NOTE: for "non-immediate" case, make sure similar process 
     * below is present in background task to complete the read. 
     */ 
    if ((status >= 0) && immed) { 
	PRINTF(("com_state %d\n",SD_xmit_info.com_state)); 
	while (SD_xmit_info.com_state) { 
	    SD_service(); 
	    if (SD_xmit_info.data_cnt >= limit 
		&& !sd_is_mmc) break; 
	}	 
	status = SD_send_cmd(SD_CMD_STOP_TRANSMIT, 0, immed); 
	PRINTF(("xmit bytes %d, required bytes %d, status\n",  
		 SD_xmit_info.data_cnt, SD_xmit_info.data_len, status)); 
    }else{ 
	JPRINTF(("SD_read_nblk... error \n")); 
	 
    } 
 
    return (status); 
} 
 
/*  
  Function: used in SD_service() for post-processing of commands... 
	    1) command retry for errors (up to SD_MAX_RETRIES). 
	    2) send app-command if CMD55 was previously sent. 
	    3) send pending command in queue if any. 
 
  input: no arguments..but app-commands and pending commands are 
	 stored in "SD_acmdq" structure for processing. 
   
  output: 
 
  side-effects: 
*/ 
int SD_get_next_cmd(void) 
{ 
    int send=0; 
 
     
    if (SD_xmit_info.response_expected && 
	!SD_xmit_info.response_recv) { 
	if (SD_xmit_info.retry_cnt < SD_MAX_RETRIES) { 
	    /* failed/aborted commands..retry */ 
	    SD_xmit_info.wait_nclk = SD_NCR_MAX; 
	    send = 1;	     
	} else { 
	    /* reach retry limit */ 
	    SD_xmit_info.retry_cnt = 0xff;  
	} 
    } else {	 
	/* command specific post-processing */ 
	switch (SD_xmit_info.cmd) { 
	case SD_CMD_APP_CMD: 
	    /* send app-command */ 
	    SD_set_cmd(SD_acmdq.cmd, SD_acmdq.arg); 
	    SD_acmdq.cmd = SD_CMD_EMPTY; /* clear cmd-Q */ 
	    send = 1; 
	    break; 
	     
	default: 
	    if (SD_acmdq.cmd != SD_CMD_EMPTY) { 
		/* cmd pending */ 
		send = SD_set_cmd(SD_acmdq.cmd, SD_acmdq.arg); 
		SD_acmdq.cmd = SD_CMD_EMPTY; /* clear cmd-Q */ 
	    } 
	    break; 
	} 
    } 
     
    return (send); 
} 
 
 
/*  
  Function: send command to SD card.   
 
  input: 
	command: command index (0-63). 
	arg:	command argument. 
	immed:	1-complete command immediately  
		0-initiate command..let SD_service() finish 
 
  output:		       
	-3:	timed out (reached retry limit) 
	-2:	communication busy 
	-1:	invalid or unsupported command or no card. 
	 0:	pending..command "passed" to SD_service().  
     	 1:	successful 
*/ 
int SD_send_cmd(int command, uint arg, int immed) 
{ 
    int status=0; 
 
 
    if (!SD_card_info.inserted || command > 63)  
	return (-1); /* sanity check */     
     
    /* send command w/ CRC7*/ 
    if ( (SD_xmit_info.com_state == SD_IDLE) ||  
	 (command == SD_CMD_STOP_TRANSMIT) ) { 
 
	if (!SD_set_cmd(command, arg)) return (-1); 
 
	SD_xmit_info.com_state = HOST2SD_START; 
	 
	if (immed) { 
	    while (SD_xmit_info.com_state) SD_service(); 
 
	    status = (SD_xmit_info.retry_cnt == 0xff) ? -3 : 1; 
	} 
    } else { 
	/* put in cmd-Q..if not app-command */ 
	if (!(command & ACMD_MARKER) &&  
	    (command != SD_CMD_APP_CMD)) { 
	    SD_acmdq.cmd = command; 
	    SD_acmdq.arg = arg; 
	} else { 
	    status = -2; /* Bus is busy..lost app-command */ 
	} 
    } 
 
    return (status); 
} 
 
 
/*  
  Function: send application specific command to SD card. 
	    App-commands needs to send pre-command(0x55). 
 
  input: 
	command: app-command index. 
	arg:	app-command argument. 
	immed:	1-complete command immediately  
		0-initiate command..let SD_service() finish 
 
  output:		       
	-2:	communication busy 
	-1:	invalid command. 
	 0:	pending..command "passed" to SD_service().  
     	 1:	successful 
*/ 
int SD_send_acmd(int acmd, uint arg, int immed) 
{ 
    int status; 
     
    /* set app-command */ 
    SD_acmdq.cmd = acmd; 
    SD_acmdq.arg = arg; 
    status = SD_send_cmd(SD_CMD_APP_CMD, SD_rca_reg<<16, immed); 
     
    return (status); 
} 
 
/*  
  Function: Initializes xmit data structure for new command.  
	    Checks card(CSD reg) for command class support. 
 
  input: 
	command: command index. 
	arg:	command argument. 
 
  output:		       
	 0:	command not supported by card. 
     	 1:	successful 
*/ 
static int SD_set_cmd(int command, uint arg)  
{ 
    uint is_acmd, cmd, tmp; 
    uchar *sd_resp_ptr; 
     
 
    cmd = command & 0x3f; 
    is_acmd = command & ACMD_MARKER; 
 
    if (SD_card_info.capacity) { 
	/* card has been initialized..check class support */ 
	tmp = (is_acmd) ? SD_CL8 : SD_cmd2class[cmd]; /* class */ 
	if (!(SD_csd_reg_ptr->ccc & (1<>4); 
    SD_xmit_info.wait_nclk = SD_NCR_MAX; 
    SD_xmit_info.cmd_sent = 0;  
    SD_xmit_info.retry_cnt = 0;     
 
    if (SD_xmit_info.use_data == 2) { /* data read */ 
	/* data destination */ 
	if (SD_xmit_info.cmd == SD_ACMD_SD_STATUS) { 
	    SD_xmit_info.cur_blk_size = 6; /* 64Bytes */ 
	    SD_xmit_info.rd_dest_ptr = (uint *)SD_status_ptr; 
	} else if (SD_xmit_info.cmd == SD_ACMD_SEND_SCR) { 
	    SD_xmit_info.cur_blk_size = 3; /* 8Bytes */ 
	    SD_xmit_info.rd_dest_ptr = (uint *)SD_scr_reg_ptr; 
	} 
    } else if (!SD_xmit_info.use_data 
	       || SD_xmit_info.response_expected) {  
	/* response data destination */ 
	if (SD_xmit_info.response_expected == SD_R2) { 
	    SD_xmit_info.rd_dest_ptr =  
		(SD_xmit_info.cmd == SD_CMD_SEND_CSD) ? 
		(uint *)SD_csd_reg_ptr : (uint *)SD_cid_reg_ptr; 
	} else { 
	    if (SD_xmit_info.response_expected == SD_R3){ 
		SD_xmit_info.rd_dest_ptr = (uint *)SD_ocr_reg_ptr; 
	    } else if (SD_xmit_info.response_expected == SD_R6){ 
		/* R6 is special..*/ 
		SD_xmit_info.rd_dest_ptr = &SD_rdata32; 
	    } else { /* R1 or R1b */ 
		SD_xmit_info.rd_dest_ptr = (uint *)SD_card_status_ptr; 
	    } 
	} 
    } 
 
    return(1); 
} 
 
static int SD_slow_clk_data_out(uchar out_data) 
{   
    int i; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    for(i=0; i<8; i++){ 
	if (out_data & 0x80) { 
	    SET_SD_CMD; 
	} else { 
	    CLEAR_SD_CMD; 
	}	 
	out_data <<= 1; 
	CLEAR_SD_CLK; 
	risc_sleep_a_bit(USEC(2)); 
	SET_SD_CLK; 
	risc_sleep_a_bit(USEC(2)); 
    } 
} 
/*********************************************** 
 up to 1 DWORD 
************************************************/ 
static int SD_slow_clk_data_in(int bit_cnt) 
{ 
    int data_in = 0, i; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    for (i=0; i>24) & 0xff; 
    *pChar++ = (arg>>16)& 0xff; 
    *pChar++ = (arg>>8) &0xff; 
    *pChar++ = arg & 0xff; 
    sd_crc = SD_calc_crc7(cmd_buf, 5); 
 
    for (i=0; i<5; i++) 
	SD_slow_clk_data_out(cmd_buf[i]); 
 
    tmp = (sd_crc<<1) | 1; 
    /* crc7 + end bit */ 
    SD_slow_clk_data_out(tmp); 
 
    SD_CMD_INPUT; /* get ready for response */ 
    if(SD_card_info.type == SD_MMC 
       && command == SD_CMD_SEND_REL_ADDR) 
	sd_timeout = SD_NCR_MAX; 
    else 
	sd_timeout = SD_NID_MAX; 
 
     
    for (i = 0; (i < sd_timeout); i++) { 
	SD_slow_clk_pulse_l2h(); 
	 
	if (SD_CMD_LOW) { /* response start bit */ 
	    /* xmit bit */ 
	    SD_slow_clk_pulse_l2h(); 
	    /* get SD response */	 
	    sd_crc = 0; 
	    break; 
	} 
    } 
 
    if (i >= sd_timeout){ 
	/* timed out..*/ 
	JPRINTF(("clk_res: timed out\n")); 
	return (-1); 
    } else { 
	cnt = 0; 
	crc_check = 0; 
	if (command == SD_CMD_ALL_SEND_CID 
	    ||command == SD_CMD_SEND_CID ) { 
	    cnt = 3; /* extra data for R2 response */ 
	    sd_rdata_ptr = (uint *)SD_cid_reg_ptr; 
	} else if ((command == SD_ACMD_SD_SEND_OP_COND) || 
		   (command == SD_CMD_MMC_SEND_OP_COND)) { 
	    sd_rdata_ptr = (uint *)SD_ocr_reg_ptr; 
	    /* R3 */ 
	} else if (command == SD_CMD_SEND_REL_ADDR 
		   && SD_card_info.type == SD_MEM) { 
	    sd_rdata_ptr = &SD_rdata32; 
	    /* R6 */ 
	    crc_check = 1; 
	} else { /* R1 (SD_CMD_APP_CMD)*/ 
	    sd_rdata_ptr = (uint *)SD_card_status_ptr; 
	    crc_check = 1; 
	} 
    } 
 
     
    /* clock in cmd and arg (38bits)..save only arg (last 32bits) */ 
    pChar = rcv_buf; 
 
    data_in = SD_slow_clk_data_in(6); 
    *pChar++ = data_in; 
    rcv_cnt = 1; 
 
    rdata_pchar = (char*)sd_rdata_ptr; 
    for(j=0; j<4; j++){ 
	data_in = SD_slow_clk_data_in(8); 
	*pChar++ = data_in; 
	rcv_cnt++; 
	*rdata_pchar++ = data_in; 
    } 
     
    cnt <<= 2; 
    while (cnt){ 
	*rdata_pchar++ = SD_slow_clk_data_in(8) ; 
	cnt--; 
	if (!cnt) goto sd_exit; 
    } 
 
    /* crc7 + end bit */ 
    data_in = SD_slow_clk_data_in(8) ; 
     
#if CHECK_RECV_CRC  
    if (!crc_check) { 
	sd_crc = 0x7f; /* no CRC for R3 response */ 
    }else 
	sd_crc = SD_calc_crc7(rcv_buf, rcv_cnt); 
    data_in >>= 1; /* remove end bit */ 
    PRINTF(("Received crc: %x, Computed crc: %x\n", data_in, sd_crc)); 
    if (data_in != sd_crc) { 
	/* crc error */ 
	CPRINTF(("CRC error in SD_clk_restricted_cmd()\n")); 
	CPRINTF(("cmd: %d, arg: %\n", command, arg)); 
	CPRINTF(("xmit crc: 0x%x, gen crc: 0x%x\n", data_in, sd_crc)); 
	BREAKPOINT(0); 
	return (0); 
    } 
#endif 
 
    if (command == SD_CMD_SEND_REL_ADDR 
	&& SD_card_info.type == SD_MEM) { 
	sd_rdata_ptr = (uint *)SD_card_status_ptr; 
	*sd_rdata_ptr &= (~0x1fff); /* bit 0-12 */ 
	*sd_rdata_ptr |= (SD_rdata32 & 0x1fff); 
	SD_card_status_ptr->error =  
	    (SD_rdata32>>13) & 1; 
	SD_card_status_ptr->illegal_cmd =  
	    (SD_rdata32>>14) & 1; 
	SD_card_status_ptr->com_crc_error =  
	    (SD_rdata32>>15) & 1; 
	 
	SD_rca_reg = SD_rdata32>>16; /* new RCA */ 
    } 
 
sd_exit: 
    /* extra 8 Clocks for SD card post-processing */ 
    for (i = 0; i < 8; i++) { 
	SD_slow_clk_pulse_l2h(); 
    } 
 
    return (1);  
} 
 
 
static void SD_cmd_output(uchar out_cmd) 
{ 
    int i; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    for (i=0; i<8; i++) { 
	if (out_cmd &0x80) { 
	    SET_SD_CMD; 
	} else { 
	    CLEAR_SD_CMD; 
	} 
	PULSE_SD_CLK_HL; 
	out_cmd <<= 1; 
    } 
} 
static void SD_1_bit_data_out(uchar out_data) 
{ 
    int i; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    for (i=0; i<8; i++) { 
	CLEAR_SD_CLK; 
	if (out_data & 0x80) { 
	    SET_SD_DAT0; 
	} else { 
	    CLEAR_SD_DAT0; 
	}	 
	SET_SD_CLK;	 
	out_data <<= 1; 
    } 
} 
 
static void SD_4_bit_data_out(char *pOutData, int cnt) 
{ 
    int i; 
    char out_data; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    for(i= 0; i>4) & 0xf)); 
	SET_SD_CLK; 
	CLEAR_SD_CLK; 
	SET_SD_DAT3_0( (out_data&0xf)); 
	SET_SD_CLK; 
    } 
} 
 
/***************************************************************** 
  up to 1 DWORD 
******************************************************************/ 
static int SD_1_bit_data_in(int bit_cnt) 
{ 
    int i, data_in; 
    unsigned sclk_high, sclk_low; 
 
    SD_GET_SCLK_VAL(sclk_high, sclk_low); 
 
    data_in = 0;     
    for (i=0; i>24)&0xff; 
	*pChar++ =(arg>>16)&0xff; 
	*pChar++ =(arg>>8)&0xff; 
	*pChar = arg & 0xff; 
	sd_crc[0] = SD_calc_crc7(cmd_buf, 5); 
 
	SD_xmit_info.com_state = HOST2SD_CMD; 
	SD_xmit_info.result = -1; /* need to set to be 0 when succeed,  
                                     currently only used in case of Erase */ 
	/*break; save time..let it drop*/ 
    case HOST2SD_CMD: 
	sd_cnt++; /* for timeout/mode check */ 
	/* command index/argument..8bits at a time */ 
	for(i=0; i<5; i++) 
	    SD_cmd_output(cmd_buf[i]); 
  
	SD_xmit_info.com_state = HOST2SD_CRC7; 
	break; 
     
    case HOST2SD_CRC7: 
	/* send crc7 */ 
	tmp = (sd_crc[0]<<1) | 1; /* crc7 + end bit */ 
	SD_cmd_output(tmp); 
 
	CLEAR_SD_CLK; 
	if (SD_xmit_info.response_expected) {	     
	    SD_CMD_INPUT; /* tristate cmd */ 
 
	    if (SD_xmit_info.use_data == 2) {  
		/* data read */ 
		if (SD_xmit_info.data_width) { 
		    /* all four data lines used */ 
		    SD_DAT3_0_INPUT; 
		} else { 
		    SD_DAT0_INPUT; 
		} 
		sd_cmd_info = 0; /* bytes received */ 
		SD_xmit_info.wait_nclk = SD_card_info.n_ac_max; 
	    }  
	    else if(SD_xmit_info.response_expected == SD_R1b){ 
		SD_DAT0_INPUT; /* busy check */ 
	    } 
 
	    sd_rdata_ptr = SD_xmit_info.rd_dest_ptr; 
	    SD_xmit_info.com_state = HOST2SD_WAIT; 
	} else { 
	    SD_xmit_info.com_state = HOST2SD_8CYCLES; 
	} 
	break; 
	 
    case HOST2SD_WAIT: 
	/* check 8 cycles at a time */ 
      
	for (i = 0; SD_xmit_info.wait_nclk; i++) { 
	    SD_LOCK15; 
	    SET_SD_CLK; 
	    if(sd_is_mmc){ 
		CLEAR_SD_CLK; 
		sd_cmd_high = SD_CMD_HIGH? 1:0; 
		sd_dat0_high = SD_DAT0_HIGH? 1:0; 
	    }else{ 
		sd_cmd_high = SD_CMD_HIGH? 1:0; 
		sd_dat0_high = SD_DAT0_HIGH? 1:0; 
		CLEAR_SD_CLK; 
	    }	 
 
	    if (SD_xmit_info.use_data != 2 
		&& !SD_xmit_info.response_recv 
		&& !sd_cmd_high){ /* response start bit */ 
		 
		SD_LOCK15; 
		SET_SD_CLK; 
		if(sd_is_mmc){ 
		    CLEAR_SD_CLK; 
		    sd_cmd_high = SD_CMD_HIGH? 1:0; 
		}else{ 
		    sd_cmd_high = SD_CMD_HIGH? 1:0; 
		    CLEAR_SD_CLK; 
		} 
		 
		/* get SD response */ 
		sd_crc[0] = 0;	 
		sd_mask = 6; /* bit count */ 
		sd_cmd_info = 0; /* clear received bits */	  
		sd_cnt = 0; /* for timeout/mode check */ 
		SD_xmit_info.com_state = SD2HOST_NODATA; 
		break; 
	    } else if ((SD_xmit_info.use_data==2)  
		       && !sd_dat0_high) { /* data start bit? */ 
 
		sd_crc[0] = 0; /* clear crc */ 
		/* current block size (bytes) */ 
	        if(SD_xmit_info.cmd == MMC_CMD_READ_UNTIL_STOP) 
		    sd_mask = SD_xmit_info.data_len; 
		else 
		    sd_mask = 1 << (SD_xmit_info.cur_blk_size); 
		if(SD_xmit_info.data_width){ 
		    SD_xmit_info.com_state = SD2HOST_DATA4; 
		} 
		else{ 
		    SD_xmit_info.com_state = SD2HOST_DATA1; 
		} 
		break; 
	    } 
	    /* btan: After received response, we actually don't need wait n_wr_max clk. * 
	     * 4 clk is ok for my SD. I did not test other SD. If you meet problem in   * 
	     * SD writing function, please increase it or let me know. */ 
	    else if (SD_xmit_info.use_data != 2 && SD_xmit_info.response_recv  
		     && sd_cmd_high && SD_xmit_info.wait_nclk > 4) { 
		SD_xmit_info.wait_nclk = 4; 
	    } 
	    SD_xmit_info.wait_nclk--; 
	} 
 
	if (!SD_xmit_info.wait_nclk) { 
#ifdef  SD_ENABLE_WRITE 
	    if (SD_xmit_info.use_data == 1 
		&& SD_xmit_info.response_recv) { 
		/* prepare to write.. */ 
		 
		/* current block size (bytes) */ 
		sd_cmd_info = 1 << (SD_xmit_info.cur_blk_size);		 
		if (SD_xmit_info.data_width) { 
		    /* write wide bus */ 
		    CLEAR_SD_DAT3_0; 
		    SD_xmit_info.com_state = HOST2SD_DATA4; 
		} else { 
		    CLEAR_SD_DAT0; 
		    SD_xmit_info.com_state = HOST2SD_DATA1; 
		}		     
		break;		 
	    } else  
#endif /* SD_ENABLE_WRITE */ 
	    { 
		/* timed out..send again?? */ 
                JPRINTF(("SD res:time out. xmit_len %d, xmit_cnt %d cmd %d\n", 
			 SD_xmit_info.data_len, SD_xmit_info.data_cnt, 
			 SD_xmit_info.cmd)); 
		SD_xmit_info.retry_cnt++; 
		SD_xmit_info.com_state = HOST2SD_8CYCLES; 
	    } 
	} 
	break; 
 
#ifdef SD_ENABLE_WRITE 
    case HOST2SD_DATA1:	 
	/* write data, one byte at a time */ 
	sd_cmd_info = 1 << (SD_xmit_info.cur_blk_size); 
	pChar = (char*)SD_xmit_info.wr_src_ptr + SD_xmit_info.data_cnt; 
	/* crc16 calculation for narrow bus */ 
	sd_crc[0] = SD_calc_crc16((uchar *)pChar,sd_cmd_info); 
        JPRINTF(("Xmit src %x, len %d\n", pChar, sd_cmd_info)); 
	/* send start bit */ 
	    CLEAR_SD_DAT0; 
	    CLEAR_SD_CLK; 
	    SET_SD_CLK; 
	for(i=0; i>2, sd_crc); 
	/* send start bit */ 
	    CLEAR_SD_DAT3_0; 
	    CLEAR_SD_CLK; 
	    SET_SD_CLK; 
 
	SD_4_bit_data_out(pChar, sd_cmd_info); 
 
	SD_xmit_info.data_cnt += sd_cmd_info; /* monitor total bytes */ 
	/* set wait timeout cycles..in case multi-block */ 
	SD_xmit_info.wait_nclk = SD_card_info.n_wr_max;  
	/* finished data "block"..get CRC */ 
	SD_xmit_info.com_state = HOST2SD_CRC16_DATA; 
 
	break; 
 
    case HOST2SD_CRC16_DATA: 
	/* send crc16 */ 
	if (SD_xmit_info.data_width){ 
	    /* wide bus */ 
	    uint filter = 0x8000; 
	    for (i=0; i<16; i++){ 
		CLEAR_SD_CLK; 
		tmp = 0; 
                for(j=0; j<4; j++){ 
		    tmp |= ( (sd_crc[j]&filter?1:0)<>= 1;	 
	    } 
	    /* send end bit */ 
	    CLEAR_SD_CLK; 
	    SET_SD_DAT3_0(0xf); /* set dat[3:0] high */ 
	    SET_SD_CLK; 
 
	    SD_DAT0_INPUT;     
	} else { 
	    /* narrow bus */ 
	    SD_1_bit_data_out(sd_crc[0]>>8); 
	    SD_1_bit_data_out(sd_crc[0]&0xff); 
	    /* send end bit */ 
	    CLEAR_SD_CLK; 
	    SET_SD_DAT0; 
	    SET_SD_CLK; 
 
	    SD_DAT0_INPUT;     
	} 
	PRINTF(("wait to get crc back \n")); 
	/* 2 clks..then there should be CRC check start bit */ 
	PULSE_SD_CLK;	     
	PULSE_SD_CLK; 
 
	/* crc status: 010: Ok 
                       101: Transimission error 
                       111: Error */ 
	wr_feedback_crc = SD_1_bit_data_in(5); 
	 
	sd_cnt = 0; 
        if(wr_feedback_crc != 0x5){ 
	    JPRINTF(("crc after xmit CRC16: %x\n",wr_feedback_crc)); 
	    SD_xmit_info.com_state = HOST2SD_8CYCLES; 
	} 
	else{ 
	    SD_xmit_info.wait_nclk = 100*SD_card_info.n_wr_max;  
	    SD_xmit_info.com_state = HOST2SD_CHECK_BUSY; 
	} 
	break; 
#endif /* SD_ENABLE_WRITE */ 
    case HOST2SD_CHECK_BUSY: 
	 
#if 0 
	/* Reading performance will be impacted */ 
	RISC_sleep_nsec(3000000);	 
#endif 
	sd_cnt++; /* for timeout/mode check */ 
	for (i=0; i<8; i++) { 
	    PULSE_SD_CLKZ; 
	    if (SD_DAT0_HIGH) { /* Busy bit */ 
		/* SD not Busy..continue */ 
		/* set next_state.. 
		 *  - for single-block write, prepare to go idle  
		 *  - for multi-block write, write another block 
		 */ 
		PRINTF((" Not busy ... xmit_cnt %d\n",  
			 SD_xmit_info.data_cnt)); 
		 
		if(SD_xmit_info.cmd == SD_CMD_WRITE_MULTI_BLOCK 
		   && SD_xmit_info.data_cnt < SD_xmit_info.data_len){ 
		    SD_xmit_info.wait_nclk = SD_card_info.n_wr_max;  
		    SD_xmit_info.com_state = HOST2SD_WAIT; 
		} 
		else{ 
		    SD_xmit_info.com_state = HOST2SD_8CYCLES; 
		    SD_xmit_info.result = 0; 
		} 
		/* single block write  or erase*/ 
		break; 
	    } 
	} 
 
	/*consider a timeout mechanism for this state */ 
	 
	if (sd_cnt > SD_xmit_info.wait_nclk){ 
	    JPRINTF(("Check Bus Busy: Time out\n")); 
	    SD_xmit_info.com_state = HOST2SD_8CYCLES;  
	}	 
	break;	 
    case SD2HOST_NODATA: 
	sd_cnt++; /* for timeout/mode check */ 
	/* response: command index/arg..8bits at a time */ 
	data_in = 0; 
        
	for (i=0; (i<8) && sd_mask; i++) { 
	    SD_LOCK15;	     
            SET_SD_CLK; 
	    if(sd_is_mmc){ 
		CLEAR_SD_CLK; 
		sd_cmd_high = SD_CMD_HIGH? 1:0; 
	    }else{ 
		sd_cmd_high = SD_CMD_HIGH? 1:0; 
		CLEAR_SD_CLK; 
	    } 
 
	    data_in <<= 1; 
	    data_in |= sd_cmd_high; 
	    sd_mask--; /* bit count remain */ 
	} 
	 
	if (i==8) { /* one arg byte in..cmd index only has 6 bits */ 
	    SD_rdata32 <<= 8; 
	    SD_rdata32 |= data_in; 
 
	    sd_cmd_info += 8; /* received bits */	     
	    if (sd_cmd_info == 32) { /* received 32 bits */ 
		*sd_rdata_ptr++ = SD_rdata32; 
		/* for 32 bit data we won't get anymore */ 
		sd_cmd_info = 0; /* reset */ 
	    } 
	} 
	 
	if (!sd_mask) { 
	    if (sd_cnt == 1) { 
		/* finished cmd..get arg */ 
		if (SD_xmit_info.response_expected == SD_R2) { 
		    sd_mask = 120; /* CID/CSD bit count */ 
		    sd_crc[0] = 0; /* CRC7 on arg data only */ 
		} else { 
		    sd_mask = 32; /* bit count */ 
		} 
		sd_cmd_info = 0; /* clear received bits */ 
		 
		/* no state change..SD_xmit_info.com_state */ 
	    } else if ((sd_cnt == 16) && 
		       (SD_xmit_info.response_expected == SD_R2)) { 
		/* finished CID/CSD register..next CRC7 */ 
		sd_crc[1] = sd_crc[0]; /* save current crc */ 
		sd_mask = 8; /* internal CRC7 + end bit */ 
 
		/* no state change..SD_xmit_info.com_state */ 
	    } else { 
		/* finished arg.. */ 
		if (SD_xmit_info.response_expected == SD_R2) { 
		    /* internal crc */ 
#if SD_CHECK_RECV_CRC 
		    data_in =  
			(SD_xmit_info.cmd == SD_CMD_SEND_CSD) ? 
			(uint)SD_csd_reg_ptr->crc :  
			(uint)SD_cid_reg_ptr->crc; 
		     
		    data_in &= 0x7f; /* 7bit crc */ 
		    sd_crc[1] &= 0x7f; /* 7bit crc */ 
		    if (data_in != sd_crc[1]) { 
			/* crc error */ 
			SD_xmit_info.crc7_err_cnt++; 
			CPRINTF(("R2 CRC7 error in SD_service()\n")); 
			CPRINTF(("xmit crc: 0x%x, gen crc: 0x%x\n", 
				 data_in, sd_crc[1])); 
			BREAKPOINT(0); 
		    } 
#endif /* SD_CHECK_RECV_CRC */ 
		    SD_xmit_info.response_recv = 1; 
		    SD_xmit_info.com_state = HOST2SD_8CYCLES; 
		} else { 
		    /* R6 response data */ 
		    if (SD_xmit_info.response_expected == SD_R6) { 
			sd_rdata_ptr = (uint *)SD_card_status_ptr; 
			*sd_rdata_ptr &= (~0x1fff); 
			*sd_rdata_ptr |= (SD_rdata32 & 0x1fff); 
			SD_card_status_ptr->error =  
			    (SD_rdata32>>12) & 1; 
			SD_card_status_ptr->illegal_cmd =  
			    (SD_rdata32>>13) & 1; 
			SD_card_status_ptr->com_crc_error =  
			    (SD_rdata32>>14) & 1; 
 
			SD_rca_reg = SD_rdata32>>16;			 
		    } 
 
		    if (SD_xmit_info.response_expected == SD_R3) { 
			/* R3 response has no CRC7 */ 
			sd_crc[0] = 0x7f; 
		    } 
		    /* finished arg..get CRC */ 
		    SD_xmit_info.com_state = SD2HOST_CRC7_NODATA; 
		} 
	    } 
	} 
	break; 
 
    case SD2HOST_DATA1:	 
	/* just get data, 32*sd_maskbits at a time..ignore response */ 
          
	 PRINTF(("SD2HOST_DATA1: xmit_len %d, xmit_data %d sd_mask %d\n", 
		  SD_xmit_info.data_len, SD_xmit_info.data_cnt, sd_mask)); 
	  
	 sd_cmd_info = sd_mask; 
	 j = (sd_mask+3)/4; 
	 if(sd_is_mmc){ 
	     do{ 
		 data_in = 0;     
		 for (i=0; (i<32); i++){ 
		     data_in <<= 1; 
		     SD_LOCK15; 
		     SET_SD_CLK; 
		     CLEAR_SD_CLK; 
		     asm("nop"); asm("nop");asm("nop"); 
		     data_in |= SD_DAT0; 
		 }      
		 *sd_rdata_ptr++ = data_in; 
	     }while(--j>0); 
	  
	 }else{ 
	     do{ 
		 data_in = 0;     
		 for (i=0; (i<32); i++){ 
		     data_in <<= 1; 
		     SD_LOCK15; 
		     SET_SD_CLK; 
		     data_in |= SD_DAT0; 
		     CLEAR_SD_CLK; 
		 }      
		 *sd_rdata_ptr++ = data_in; 
	     }while(--j>0); 
	 } 
	 PRINTF(("SD_xmit_cmd %d\n", SD_xmit_info.cmd)); 
	 if(SD_xmit_info.cmd == SD_CMD_READ_MULTI_BLOCK){ 
	     SD_xmit_info.data_cnt += sd_cmd_info; 
	     PRINTF(("SD_xmit_cmd %d\n", SD_xmit_info.cmd)); 
	 } 
	 else{ 
	     SD_xmit_info.data_cnt = sd_cmd_info; 
	 } 
	 	 
	 /* set wait timeout cycles..in case multi-block */ 
	 SD_xmit_info.wait_nclk = SD_card_info.n_ac_max; 
	  
	 /* finished arg..get CRC */ 
	 if(SD_xmit_info.cmd == MMC_CMD_READ_UNTIL_STOP){ 
	     SD_xmit_info.com_state = SD_IDLE; 
	 }else{ 
	     SD_xmit_info.com_state = SD2HOST_CRC16_DATA; 
	     PRINTF(("go to get CRC16\n")); 
	 } 
	 break; 
 
    case SD2HOST_DATA4:	 
	/* just get data, 32bits at a time..ignore response */ 
	sd_cmd_info += sd_mask; 
	sd_mask >>= 2; 
	do{ 
	    data_in = 0; 
	    for (i=0; (i<8); i++){	 
		SD_LOCK15; 
		SET_SD_CLK; 
		data_in <<= 4; 
		data_in |= SD_DAT3_0; 
		CLEAR_SD_CLK; 
	    } 
	    *sd_rdata_ptr++ = data_in; 
	    sd_mask--; /* bytes remain */ 
	}while(sd_mask>0); 
	SD_xmit_info.data_cnt = sd_cmd_info; /* monitor total bytes */ 
	 
	/* set wait timeout cycles..in case multi-block */ 
	SD_xmit_info.wait_nclk = SD_card_info.n_ac_max;  
	/* finished data block..get CRC */ 
	SD_xmit_info.com_state = SD2HOST_CRC16_DATA; 
	break; 
 
    case SD2HOST_CRC7_NODATA: 
	/* get crc7 */ 
	data_in = 0; 
	/* clock in crc7 + end bit */ 
  
	for (i=0; (i < 8); i++) { 
	    SD_LOCK15; 
	    SET_SD_CLK; 
	    if(sd_is_mmc){ 
		CLEAR_SD_CLK; 
		sd_cmd_high = SD_CMD_HIGH ?1:0; 
	    }else{ 
		sd_cmd_high = SD_CMD_HIGH ?1:0; 
		CLEAR_SD_CLK; 
	    } 
#if SD_CHECK_RECV_CRC 
	    data_in <<= 1; 
	    data_in |= sd_cmd_high; 
#endif 
	} 
	SD_xmit_info.response_recv = 1; 
	if(SD_xmit_info.response_expected == SD_R1b){ 
 
	    if(SD_xmit_info.cmd == SD_CMD_ERASE 
	   && (SD_card_status_ptr->erase_seq_error 
	       || SD_card_status_ptr->erase_param) 
	       ){ 
		JPRINTF(("Erase error...\n")); 
		SD_xmit_info.com_state = HOST2SD_8CYCLES; 
	    }else{ 
		SD_xmit_info.wait_nclk = 100*SD_card_info.n_wr_max;  
		SD_xmit_info.com_state = HOST2SD_CHECK_BUSY; 
		JPRINTF(("R1b: to check busy state: card_status %x, cmd %d \n", 
			 *(int*)SD_card_status_ptr, SD_xmit_info.cmd)); 
	    } 
	}else{ 
#ifdef SD_ENABLE_WRITE 
	    if (SD_xmit_info.cmd == SD_CMD_WRITE_BLOCK 
		||SD_xmit_info.cmd == SD_CMD_WRITE_MULTI_BLOCK ) {  
		/* data write */ 
		if (SD_xmit_info.data_width) { 
		    SET_SD_DAT3_0(0xf); /* set dat[3:0] high */ 
		} else { 
		    SET_SD_DAT0; 
		} 
		SD_xmit_info.wait_nclk = SD_card_info.n_wr_max; 
		SD_xmit_info.com_state = HOST2SD_WAIT; 
		SD_xmit_info.data_cnt = 0; 
		JPRINTF(("Wait to write data, status %x\n", 
			 *(int*)SD_card_status_ptr)); 
	    }else 
#endif /* SD_ENABLE_WRITE */ 
		SD_xmit_info.com_state = HOST2SD_8CYCLES; 
 
	} 
	break; 
 
    case SD2HOST_CRC16_DATA: 
	/* get crc16 */ 
	data_in = 0; 
	/* clock in crc16 + end bit */ 
        PRINTF(("SD2HOST_CRC16_DATA:\n")); 
	for (i=0; (i < 17); i++) { 
	    data_in <<= 1; 
	    SD_LOCK15; 
	    SET_SD_CLK; 
	    if(sd_is_mmc){ 
		CLEAR_SD_CLK; 
		data_in |= SD_DAT0; 
	    }else{ 
		data_in |= SD_DAT0; 
		CLEAR_SD_CLK; 
	    } 
	} 
	if(data_in &1 == 0) 
	JPRINTF(("End bit not found.\n")); 
	/*  
	 *  - for single-block read, prepare to go idle  
	 *  - for multi-block read, get another block 
	 */ 
	if (SD_xmit_info.cmd == SD_CMD_READ_MULTI_BLOCK) { 
	    PRINTF(("end of sector, xmit_cnt %d  xmit_len %d\n",  
		     SD_xmit_info.data_cnt,SD_xmit_info.data_len)); 
	    if(sd_is_mmc  
	       && SD_xmit_info.data_cnt>= SD_xmit_info.data_len){ 
		   SD_xmit_info.com_state = HOST2SD_8CYCLES; 
		   SD_xmit_info.response_recv = 1; 
		   PRINTF(("command->HOST2SD_8CYCLES \n")); 
	       } 
	    else{ 
		SD_xmit_info.com_state = HOST2SD_WAIT; 
	    } 
	} else { 
	    SD_xmit_info.response_recv = 1; /* prevent resend */ 
	    SD_xmit_info.com_state = HOST2SD_8CYCLES; 
	}	 
	break; 
 
    case HOST2SD_8CYCLES: 
	/* extra 8 Clocks for SD card's post-processing */ 
	for (i = 0; i < 8; i++){ 
	    SD_LOCK15; 
	    SET_SD_CLK; 
	    CLEAR_SD_CLK; 
	} 
	/* check for next command..or resend */ 
	if (SD_get_next_cmd()){ 
	    JPRINTF(("next cmd not empty\n")); 
	    SD_xmit_info.com_state = HOST2SD_START; 
	} else { 
	    SD_xmit_info.cmd_sent = 1; /* cmd completed */ 
	    SD_xmit_info.com_state = SD_IDLE; 
	}	 
	break; 
 
    case SD_IDLE: 
    default: 
	break; 
    } 
    VCX_service(); 
} 
 
int SD_get_status(void) 
{ 
    int status = 0; 
     
    status = SD_send_cmd(SD_CMD_SEND_STATUS,  SD_rca_reg<<16, 1); 
    if(status != 1) 
	return -1; 
    else 
	return (*(int*)SD_card_status_ptr); 
} 
 
#ifdef MD_ASYNC_RW 
 
 
void SD_clear_state() 
{ 
    SD_xmit_info.com_state = SD_IDLE; 
    SD_xmit_info.cmd = SD_CMD_EMPTY; 
    SD_acmdq.cmd = SD_CMD_EMPTY; /* clear cmd-Q */ 
    return; 
} 
 
/* return: 0: not finish; 1: finish; 2:read single finish; * 
 * 3:write single finish; 4: read multi finish; 5: write multi finish*/ 
int SD_cmd_service() 
{ 
    int ret = 0, status = 1; 
    if (!SD_card_info.inserted) { 
	SD_clear_state(); 
	return 1; 
    } 
    if (SD_xmit_info.com_state != SD_IDLE) SD_service(); 
    if (SD_xmit_info.com_state == SD_IDLE) { 
	if (SD_xmit_info.cmd == SD_CMD_READ_SINGLE_BLOCK) { 
	    ret = 2; 
	} 
	else if (SD_xmit_info.cmd == SD_CMD_WRITE_BLOCK) { 
	    ret = 3; 
	} 
	else if (SD_xmit_info.cmd == SD_CMD_READ_MULTI_BLOCK) { 
	    ret = 4; 
	} 
	else if (SD_xmit_info.cmd == SD_CMD_WRITE_MULTI_BLOCK) { 
	    ret = 5; 
	} 
 
	if (SD_xmit_info.cmd == SD_CMD_WRITE_MULTI_BLOCK ||  
	    SD_xmit_info.cmd == SD_CMD_READ_MULTI_BLOCK) { 
	    SD_xmit_info.cmd = SD_CMD_EMPTY; 
	    status = SD_send_cmd(SD_CMD_STOP_TRANSMIT, 0, 0); 
	} 
	else { 
	    SD_xmit_info.cmd = SD_CMD_EMPTY; 
	    if (!SD_get_next_cmd() && !ret) ret = 1; 
	} 
    } 
    else if (SD_xmit_info.cmd == SD_CMD_READ_MULTI_BLOCK && 
	     SD_xmit_info.data_cnt >= SD_xmit_info.data_len && !sd_is_mmc) { 
	status = SD_send_cmd(SD_CMD_STOP_TRANSMIT, 0, 0); 
	ret = 4; 
    } 
    return ret; 
} 
#endif /*MD_ASYNC_RW*/ 
 
 
#if NOT_USED  
/*  
  Function:  
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
int SD_get_status(void) 
{ 
    uint status = 0; 
     
    if (SD_card_info.type > SD_MMC) { 
	SD_send_acmd(SD_ACMD_SD_STATUS, 0, 1); 
    } 
 
    return (status); 
} 
 
/*  
  Function: SD lock card (password protect) 
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
int SD_lock_card(int action, char *passwd) 
{ 
    int status; 
     
    /*  lock/unlock card */ 
    return (status); 
} 
 
/*  
  Function: SD set password  
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
int SD_set_password(char *passwd) 
{ 
    int status; 
     
    /* set password */ 
    return (status); 
} 
 
/*  
  Function: SD reset password  
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
int SD_reset_password(char *passwd) 
{ 
    int status; 
     
    /* reset password  w/ and w/o password */ 
    return (status); 
} 
 
/*  
  Function:  
 
  input: 
 
  output:		       
 
  side-effects: 
*/ 
int SD_write_data(uint data) 
{ 
    int status; 
 
    /* send data w/ CRC16 */ 
    return (status); 
} 
#endif 
 
#endif /* SD_MEMORY */