cLK/lk/platform/msm_shared/mmc.c

2797 lines
87 KiB
C
Raw Permalink Normal View History

2011-03-25 21:39:33 +00:00
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <stdlib.h>
#include <debug.h>
#include <reg.h>
#include "mmc.h"
#include <platform/iomap.h>
#ifndef NULL
#define NULL 0
#endif
#define ROUND_TO_PAGE(x,y) (((x) + (y)) & (~(y)))
/* data access time unit in ns */
static const unsigned int taac_unit[] =
{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
/* data access time value x 10 */
static const unsigned int taac_value[] =
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
/* data transfer rate in kbit/s */
static const unsigned int xfer_rate_unit[] =
{ 100, 1000, 10000, 100000, 0, 0, 0, 0 };
/* data transfer rate value x 10*/
static const unsigned int xfer_rate_value[] =
{ 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80 };
char *ext3_partitions[] = {"system", "userdata", "persist"};
unsigned int ext3_count = 0;
static unsigned mmc_sdc_clk[] = { SDC1_CLK, SDC2_CLK, SDC3_CLK, SDC4_CLK};
static unsigned mmc_sdc_pclk[] = { SDC1_PCLK, SDC2_PCLK, SDC3_PCLK, SDC4_PCLK};
unsigned char mmc_slot = 0;
unsigned int mmc_boot_mci_base = 0;
static unsigned char ext_csd_buf[512];
static unsigned char wp_status_buf[8];
int mmc_clock_enable_disable(unsigned id, unsigned enable);
int mmc_clock_get_rate(unsigned id);
int mmc_clock_set_rate(unsigned id, unsigned rate);
void mdelay(unsigned msecs);
struct mmc_boot_host mmc_host;
struct mmc_boot_card mmc_card;
struct mbr_entry mbr[MAX_PARTITIONS];
unsigned mmc_partition_count = 0;
static void mbr_fill_name (struct mbr_entry *mbr_ent, unsigned int type);
unsigned int mmc_read (unsigned long long data_addr, unsigned int* out, unsigned int data_len);
static unsigned int mmc_wp(unsigned int addr, unsigned int size, unsigned char set_clear_wp);
unsigned int SWAP_ENDIAN(unsigned int val)
{
return ((val & 0xFF) << 24) |
(((val >> 8) & 0xFF) << 16) |
(((val >> 16) & 0xFF) << 8) |
(val >> 24);
}
/*
* Function to enable and set master and peripheral clock for
* MMC card.
*/
static unsigned int mmc_boot_enable_clock( struct mmc_boot_host* host,
unsigned int mclk)
{
unsigned int mmc_clk = 0;
#ifndef PLATFORM_MSM8X60
int mmc_signed_ret = 0;
unsigned SDC_CLK = mmc_sdc_clk[mmc_slot - 1];
unsigned SDC_PCLK = mmc_sdc_pclk[mmc_slot - 1];
if( host == NULL )
{
return MMC_BOOT_E_INVAL;
}
if( !host->clk_enabled )
{
/* set clock */
if( mmc_clock_enable_disable(SDC_PCLK, MMC_CLK_ENABLE) < 0 )
{
dprintf(CRITICAL, "Failure enabling PCLK!\n");
goto error_pclk;
}
if( mmc_clock_enable_disable(SDC_CLK, MMC_CLK_ENABLE) < 0 )
{
dprintf(CRITICAL, "Failure enabling MMC Clock!\n");
goto error;
}
host->clk_enabled = 1;
}
if( host->mclk_rate != mclk )
{
if( mmc_clock_set_rate(SDC_CLK, mclk) < 0 )
{
dprintf(CRITICAL, "Failure setting clock rate for MCLK - clk_rate: %d\n!", mclk );
goto error_mclk;
}
if( ( mmc_signed_ret = mmc_clock_get_rate(SDC_CLK) ) < 0 )
{
dprintf(CRITICAL, "Failure getting clock rate for MCLK - clk_rate: %d\n!", host->mclk_rate );
goto error_mclk;
}
host->mclk_rate = (unsigned int)mmc_signed_ret;
}
if( ( mmc_signed_ret = mmc_clock_get_rate(SDC_PCLK) ) < 0 )
{
dprintf(CRITICAL, "Failure getting clock rate for PCLK - clk_rate: %d\n!", host->pclk_rate );
goto error_pclk;
}
host->pclk_rate = ( unsigned int )mmc_signed_ret;
dprintf(INFO, "Clock rate - mclk: %dHz pclk: %dHz\n", host->mclk_rate, host->pclk_rate );
#else
clock_set_enable(mclk);
host->mclk_rate = mclk;
host->pclk_rate = mclk;
host->clk_enabled = 1;
#endif
//enable mci clock
mmc_clk |= MMC_BOOT_MCI_CLK_ENABLE;
//enable flow control
mmc_clk |= MMC_BOOT_MCI_CLK_ENA_FLOW;
//latch data and command using feedback clock
mmc_clk |= MMC_BOOT_MCI_CLK_IN_FEEDBACK;
writel( mmc_clk, MMC_BOOT_MCI_CLK );
return MMC_BOOT_E_SUCCESS;
#ifndef PLATFORM_MSM8X60
error_pclk:
mmc_clock_enable_disable(SDC_PCLK, MMC_CLK_DISABLE);
error_mclk:
mmc_clock_enable_disable(SDC_CLK, MMC_CLK_DISABLE);
error:
return MMC_BOOT_E_CLK_ENABLE_FAIL;
#endif
}
/* Sets a timeout for read operation.
*/
static unsigned int mmc_boot_set_read_timeout( struct mmc_boot_host* host,
struct mmc_boot_card* card )
{
unsigned int timeout_ns = 0;
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
if( (card->type == MMC_BOOT_TYPE_MMCHC) || (card->type == MMC_BOOT_TYPE_SDHC) )
{
card->rd_timeout_ns = 100000000;
}
else if( (card->type == MMC_BOOT_TYPE_STD_SD) || (card->type == MMC_BOOT_TYPE_STD_MMC) )
{
timeout_ns = 10 * ( (card->csd.taac_ns ) +
( card->csd.nsac_clk_cycle / (host->mclk_rate/1000000000)));
card->rd_timeout_ns = timeout_ns;
}
else
{
return MMC_BOOT_E_NOT_SUPPORTED;
}
dprintf(INFO, " Read timeout set: %d ns\n", card->rd_timeout_ns );
return MMC_BOOT_E_SUCCESS;
}
/* Sets a timeout for write operation.
*/
static unsigned int mmc_boot_set_write_timeout( struct mmc_boot_host* host,
struct mmc_boot_card* card )
{
unsigned int timeout_ns = 0;
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
if( (card->type == MMC_BOOT_TYPE_MMCHC) || (card->type == MMC_BOOT_TYPE_SDHC) )
{
card->wr_timeout_ns = 100000000;
}
else if( card->type == MMC_BOOT_TYPE_STD_SD || (card->type == MMC_BOOT_TYPE_STD_MMC) )
{
timeout_ns = 10 * ( ( card->csd.taac_ns ) +
( card->csd.nsac_clk_cycle / ( host->mclk_rate/1000000000 ) ) );
timeout_ns = timeout_ns << card->csd.r2w_factor;
card->wr_timeout_ns = timeout_ns;
}
else
{
return MMC_BOOT_E_NOT_SUPPORTED;
}
dprintf(INFO, " Write timeout set: %d ns\n", card->wr_timeout_ns );
return MMC_BOOT_E_SUCCESS;
}
/*
* Decodes CSD response received from the card. Note that we have defined only
* few of the CSD elements in csd structure. We'll only decode those values.
*/
static unsigned int mmc_boot_decode_and_save_csd( struct mmc_boot_card* card,
unsigned int* raw_csd )
{
unsigned int mmc_sizeof = 0;
unsigned int mmc_unit = 0;
unsigned int mmc_value = 0;
unsigned int mmc_temp = 0;
struct mmc_boot_csd mmc_csd;
if( ( card == NULL ) || ( raw_csd == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
/* CSD register is little bit differnet for CSD version 2.0 High Capacity
* and CSD version 1.0/2.0 Standard memory cards. In Version 2.0 some of
* the fields have fixed values and it's not necessary for host to refer
* these fields in CSD sent by card */
mmc_sizeof = sizeof(unsigned int) * 8;
mmc_csd.cmmc_structure = UNPACK_BITS( raw_csd, 126, 2, mmc_sizeof );
/* cmmc_structure- 0: Version 1.0 1: Version 2.0 */
if( mmc_csd.cmmc_structure )
{
mmc_csd.card_cmd_class = UNPACK_BITS( raw_csd, 84, 12, mmc_sizeof );
mmc_csd.write_blk_len = 512; /* Fixed value is 9 = 2^9 = 512 */
mmc_csd.read_blk_len = 512; /* Fixed value is 9 = 512 */
mmc_csd.r2w_factor = UNPACK_BITS( raw_csd, 26, 3, mmc_sizeof ); /* Fixed value: 010b */
mmc_csd.c_size_mult = 0; /* not there in version 2.0 */
mmc_csd.c_size = UNPACK_BITS( raw_csd, 62, 12, mmc_sizeof );
mmc_csd.nsac_clk_cycle = UNPACK_BITS( raw_csd, 104, 8, mmc_sizeof) * 100;
mmc_unit = UNPACK_BITS( raw_csd, 112, 3, mmc_sizeof );
mmc_value = UNPACK_BITS( raw_csd, 115, 4, mmc_sizeof );
mmc_csd.taac_ns = ( taac_value[mmc_value] * taac_unit[mmc_unit]) / 10;
mmc_csd.erase_blk_len = 1;
mmc_csd.read_blk_misalign = 0;
mmc_csd.write_blk_misalign = 0;
mmc_csd.read_blk_partial = 0;
mmc_csd.write_blk_partial = 0;
mmc_unit = UNPACK_BITS( raw_csd, 96, 3, mmc_sizeof );
mmc_value = UNPACK_BITS( raw_csd, 99, 4, mmc_sizeof );
mmc_csd.tran_speed = ( xfer_rate_value[mmc_value] * xfer_rate_unit[mmc_unit]) / 10;
/* Calculate card capcity now itself */
card->capacity = ( 1 + mmc_csd.c_size ) * 512000;
}
else
{
mmc_csd.card_cmd_class = UNPACK_BITS( raw_csd, 84, 12, mmc_sizeof );
mmc_temp = UNPACK_BITS( raw_csd, 22, 4, mmc_sizeof );
mmc_csd.write_blk_len = ( mmc_temp > 8 && mmc_temp < 12 )? ( 1 << mmc_temp ) : 512;
mmc_temp = UNPACK_BITS( raw_csd, 80, 4, mmc_sizeof );
mmc_csd.read_blk_len = ( mmc_temp > 8 && mmc_temp < 12 )? ( 1 << mmc_temp ) : 512;
mmc_unit = UNPACK_BITS( raw_csd, 112, 3, mmc_sizeof );
mmc_value = UNPACK_BITS( raw_csd, 115, 4, mmc_sizeof );
mmc_csd.taac_ns = ( taac_value[mmc_value] * taac_unit[mmc_unit]) / 10;
mmc_unit = UNPACK_BITS( raw_csd, 96, 3, mmc_sizeof );
mmc_value = UNPACK_BITS( raw_csd, 99, 4, mmc_sizeof );
mmc_csd.tran_speed = ( xfer_rate_value[mmc_value] * xfer_rate_unit[mmc_unit]) / 10;
mmc_csd.nsac_clk_cycle = UNPACK_BITS( raw_csd, 104, 8, mmc_sizeof ) * 100;
mmc_csd.r2w_factor = UNPACK_BITS( raw_csd, 26, 3, mmc_sizeof );
mmc_csd.sector_size = UNPACK_BITS( raw_csd, 39, 7, mmc_sizeof ) + 1;
mmc_csd.erase_blk_len = UNPACK_BITS( raw_csd, 46, 1, mmc_sizeof );
mmc_csd.read_blk_misalign = UNPACK_BITS( raw_csd, 77, 1, mmc_sizeof );
mmc_csd.write_blk_misalign = UNPACK_BITS( raw_csd, 78, 1, mmc_sizeof );
mmc_csd.read_blk_partial = UNPACK_BITS( raw_csd, 79, 1, mmc_sizeof );
mmc_csd.write_blk_partial = UNPACK_BITS( raw_csd, 21, 1, mmc_sizeof );
mmc_csd.c_size_mult = UNPACK_BITS( raw_csd, 47, 3, mmc_sizeof );
mmc_csd.c_size = UNPACK_BITS( raw_csd, 62, 12, mmc_sizeof );
mmc_temp = ( 1 << ( mmc_csd.c_size_mult + 2 ) ) * ( mmc_csd.c_size + 1 );
card->capacity = mmc_temp * mmc_csd.read_blk_len;
}
mmc_csd.erase_grp_size = UNPACK_BITS( raw_csd, 42, 5, mmc_sizeof );
mmc_csd.erase_grp_mult = UNPACK_BITS( raw_csd, 37, 5, mmc_sizeof );
mmc_csd.wp_grp_size = UNPACK_BITS( raw_csd, 32, 5, mmc_sizeof );
mmc_csd.wp_grp_enable = UNPACK_BITS( raw_csd, 31, 1, mmc_sizeof );
mmc_csd.perm_wp = UNPACK_BITS( raw_csd, 13, 1, mmc_sizeof );
mmc_csd.temp_wp = UNPACK_BITS( raw_csd, 12, 1, mmc_sizeof );
/* save the information in card structure */
memcpy( (struct mmc_boot_csd *)&card->csd, (struct mmc_boot_csd *)&mmc_csd,
sizeof(struct mmc_boot_csd) );
dprintf(INFO, "Decoded CSD fields:\n" );
dprintf(INFO, "cmmc_structure: %d\n", mmc_csd.cmmc_structure );
dprintf(INFO, "card_cmd_class: %x\n", mmc_csd.card_cmd_class );
dprintf(INFO, "write_blk_len: %d\n", mmc_csd.write_blk_len );
dprintf(INFO, "read_blk_len: %d\n", mmc_csd.read_blk_len );
dprintf(INFO, "r2w_factor: %d\n", mmc_csd.r2w_factor );
dprintf(INFO, "sector_size: %d\n", mmc_csd.sector_size );
dprintf(INFO, "c_size_mult:%d\n", mmc_csd.c_size_mult );
dprintf(INFO, "c_size: %d\n", mmc_csd.c_size );
dprintf(INFO, "nsac_clk_cycle: %d\n", mmc_csd.nsac_clk_cycle );
dprintf(INFO, "taac_ns: %d\n", mmc_csd.taac_ns );
dprintf(INFO, "tran_speed: %d kbps\n", mmc_csd.tran_speed );
dprintf(INFO, "erase_blk_len: %d\n", mmc_csd.erase_blk_len );
dprintf(INFO, "read_blk_misalign: %d\n", mmc_csd.read_blk_misalign );
dprintf(INFO, "write_blk_misalign: %d\n", mmc_csd.write_blk_misalign );
dprintf(INFO, "read_blk_partial: %d\n", mmc_csd.read_blk_partial );
dprintf(INFO, "write_blk_partial: %d\n", mmc_csd.write_blk_partial );
dprintf(INFO, "Card Capacity: %d Bytes\n", card->capacity );
return MMC_BOOT_E_SUCCESS;
}
/*
* Decode CID sent by the card.
*/
static unsigned int mmc_boot_decode_and_save_cid( struct mmc_boot_card* card,
unsigned int* raw_cid )
{
struct mmc_boot_cid mmc_cid;
unsigned int mmc_sizeof = 0;
int i = 0;
if( ( card == NULL ) || ( raw_cid == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
mmc_sizeof = sizeof( unsigned int ) * 8;
mmc_cid.mid = UNPACK_BITS( raw_cid, 120, 8, mmc_sizeof );
mmc_cid.oid = UNPACK_BITS( raw_cid, 104, 16, mmc_sizeof );
for( i = 0; i < 6; i++ )
{
mmc_cid.pnm[i] = (unsigned char) UNPACK_BITS(raw_cid, \
(104 - 8 * (i+1)), 8, mmc_sizeof );
}
mmc_cid.pnm[6] = 0;
mmc_cid.prv = UNPACK_BITS( raw_cid, 48, 8, mmc_sizeof );
mmc_cid.psn = UNPACK_BITS( raw_cid, 16, 32, mmc_sizeof );
mmc_cid.month = UNPACK_BITS( raw_cid, 12, 4, mmc_sizeof );
mmc_cid.year = UNPACK_BITS( raw_cid, 8, 4, mmc_sizeof );
/* save it in card database */
memcpy( ( struct mmc_boot_cid * )&card->cid, \
( struct mmc_boot_cid * )&mmc_cid, \
sizeof( struct mmc_boot_cid ) );
dprintf(INFO, "Decoded CID fields:\n" );
dprintf(INFO, "Manufacturer ID: %x\n", mmc_cid.mid );
dprintf(INFO, "OEM ID: 0x%x\n", mmc_cid.oid );
dprintf(INFO, "Product Name: %s\n", mmc_cid.pnm );
dprintf(INFO, "Product revision: %d.%d\n", (mmc_cid.prv >> 4), (mmc_cid.prv & 0xF) );
dprintf(INFO, "Product serial number: %X\n", mmc_cid.psn );
dprintf(INFO, "Manufacturing date: %d %d\n", mmc_cid.month, mmc_cid.year + 1997 );
return MMC_BOOT_E_SUCCESS;
}
/*
* Sends specified command to a card and waits for a response.
*/
static unsigned int mmc_boot_send_command( struct mmc_boot_command* cmd )
{
unsigned int mmc_cmd = 0;
unsigned int mmc_status = 0;
unsigned int mmc_resp = 0;
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
unsigned int cmd_index = 0;
int i = 0;
/* basic check */
if( cmd == NULL )
{
return MMC_BOOT_E_INVAL;
}
/* 1. Write command argument to MMC_BOOT_MCI_ARGUMENT register */
writel( cmd->argument, MMC_BOOT_MCI_ARGUMENT );
/* 2. Set appropriate fields and write MMC_BOOT_MCI_CMD */
/* 2a. Write command index in CMD_INDEX field */
cmd_index = cmd->cmd_index;
mmc_cmd |= cmd->cmd_index;
/* 2b. Set RESPONSE bit to 1 for all cmds except CMD0 */
if( cmd_index != CMD0_GO_IDLE_STATE )
{
mmc_cmd |= MMC_BOOT_MCI_CMD_RESPONSE;
}
/* 2c. Set LONGRESP bit to 1 for CMD2, CMD9 and CMD10 */
if( IS_RESP_136_BITS(cmd->resp_type) )
{
mmc_cmd |= MMC_BOOT_MCI_CMD_LONGRSP;
}
/* 2d. Set INTERRUPT bit to 1 to disable command timeout */
/* 2e. Set PENDING bit to 1 for CMD12 in the beginning of stream
mode data transfer*/
if( cmd->xfer_mode == MMC_BOOT_XFER_MODE_STREAM )
{
mmc_cmd |= MMC_BOOT_MCI_CMD_PENDING;
}
/* 2f. Set ENABLE bit to 1 */
mmc_cmd |= MMC_BOOT_MCI_CMD_ENABLE;
/* 2g. Set PROG_ENA bit to 1 for CMD12, CMD13 issued at the end of
write data transfer */
if( ( cmd_index == CMD12_STOP_TRANSMISSION ||
cmd_index == CMD13_SEND_STATUS ) && cmd->prg_enabled )
{
mmc_cmd |= MMC_BOOT_MCI_CMD_PROG_ENA;
}
/* 2h. Set MCIABORT bit to 1 for CMD12 when working with SDIO card */
/* 2i. Set CCS_ENABLE bit to 1 for CMD61 when Command Completion Signal
of CE-ATA device is enabled */
/* 2j. clear all static status bits */
writel( MMC_BOOT_MCI_STATIC_STATUS, MMC_BOOT_MCI_CLEAR );
/* 2k. Write to MMC_BOOT_MCI_CMD register */
writel( mmc_cmd, MMC_BOOT_MCI_CMD );
dprintf(INFO, "Command sent: CMD%d MCI_CMD_REG:%x MCI_ARG:%x\n",
cmd_index, mmc_cmd, cmd->argument );
/* 3. Wait for interrupt or poll on the following bits of MCI_STATUS
register */
do{
/* 3a. Read MCI_STATUS register */
while(readl( MMC_BOOT_MCI_STATUS ) \
& MMC_BOOT_MCI_STAT_CMD_ACTIVE);
mmc_status = readl( MMC_BOOT_MCI_STATUS );
/* 3b. CMD_SENT bit supposed to be set to 1 only after CMD0 is sent -
no response required. */
if( ( cmd->resp_type == MMC_BOOT_RESP_NONE ) &&
(mmc_status & MMC_BOOT_MCI_STAT_CMD_SENT ) )
{
break;
}
/* 3c. If CMD_TIMEOUT bit is set then no response was received */
else if( mmc_status & MMC_BOOT_MCI_STAT_CMD_TIMEOUT )
{
mmc_return = MMC_BOOT_E_TIMEOUT;
break;
}
/* 3d. If CMD_RESPONSE_END bit is set to 1 then command's response was
received and CRC check passed
Spcial case for ACMD41: it seems to always fail CRC even if
the response is valid
*/
else if (( mmc_status & MMC_BOOT_MCI_STAT_CMD_RESP_END ) || (cmd_index == CMD1_SEND_OP_COND)
|| (cmd_index == CMD8_SEND_IF_COND))
{
/* 3i. Read MCI_RESP_CMD register to verify that response index is
equal to command index */
mmc_resp = readl( MMC_BOOT_MCI_RESP_CMD ) & 0x3F;
/* However, long response does not contain the command index field.
* In that case, response index field must be set to 111111b (0x3F) */
if( ( mmc_resp == cmd_index ) ||
( cmd->resp_type == MMC_BOOT_RESP_R2 ||
cmd->resp_type == MMC_BOOT_RESP_R3 ||
cmd->resp_type == MMC_BOOT_RESP_R6 ||
cmd->resp_type == MMC_BOOT_RESP_R7 ) )
{
/* 3j. If resp index is equal to cmd index, read command resp
from MCI_RESPn registers
- MCI_RESP0/1/2/3 for CMD2/9/10
- MCI_RESP0 for all other registers */
if( IS_RESP_136_BITS( cmd->resp_type ) )
{
for( i = 0; i < 4; i++ )
{
cmd->resp[3-i] = readl( MMC_BOOT_MCI_RESP_0 + ( i * 4 ) );
}
}
else
{
cmd->resp[0] = readl( MMC_BOOT_MCI_RESP_0 );
}
}
else
{
/* command index mis-match */
mmc_return = MMC_BOOT_E_CMD_INDX_MISMATCH;
}
dprintf(INFO, "Command response received: %X\n", cmd->resp[0] );
break;
}
/* 3e. If CMD_CRC_FAIL bit is set to 1 then cmd's response was recvd,
but CRC check failed. */
else if( ( mmc_status & MMC_BOOT_MCI_STAT_CMD_CRC_FAIL ) )
{
if(cmd_index == ACMD41_SEND_OP_COND)
{
cmd->resp[0] = readl( MMC_BOOT_MCI_RESP_0);
}
else
mmc_return = MMC_BOOT_E_CRC_FAIL;
break;
}
}while(1);
return mmc_return;
}
/*
* Reset all the cards to idle condition (CMD 0)
*/
static unsigned int mmc_boot_reset_cards( void )
{
struct mmc_boot_command cmd;
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
cmd.cmd_index = CMD0_GO_IDLE_STATE;
cmd.argument = 0; // stuff bits - ignored
cmd.cmd_type = MMC_BOOT_CMD_BCAST;
cmd.resp_type = MMC_BOOT_RESP_NONE;
/* send command */
return mmc_boot_send_command( &cmd );
}
/*
* Send CMD1 to know whether the card supports host VDD profile or not.
*/
static unsigned int mmc_boot_send_op_cond( struct mmc_boot_host* host,
struct mmc_boot_card* card )
{
struct mmc_boot_command cmd;
unsigned int mmc_resp = 0;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD1 format:
* [31] Busy bit
* [30:29] Access mode
* [28:24] reserved
* [23:15] 2.7-3.6
* [14:8] 2.0-2.6
* [7] 1.7-1.95
* [6:0] reserved
*/
cmd.cmd_index = CMD1_SEND_OP_COND;
cmd.argument = host->ocr;
cmd.cmd_type = MMC_BOOT_CMD_BCAST_W_RESP;
cmd.resp_type = MMC_BOOT_RESP_R3;
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Now it's time to examine response */
mmc_resp = cmd.resp[0];
/* Response contains card's ocr. Update card's information */
card->ocr = mmc_resp;
/* Check the response for busy status */
if( !( mmc_resp & MMC_BOOT_OCR_BUSY ) )
{
return MMC_BOOT_E_CARD_BUSY;
}
if(mmc_resp & MMC_BOOT_OCR_SEC_MODE)
{
card->type = MMC_BOOT_TYPE_MMCHC;
}
else
{
card->type = MMC_BOOT_TYPE_STD_MMC;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Request any card to send its uniquie card identification (CID) number (CMD2).
*/
static unsigned int mmc_boot_all_send_cid( struct mmc_boot_card* card )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD2 Format:
* [31:0] stuff bits
*/
cmd.cmd_index = CMD2_ALL_SEND_CID;
cmd.argument = 0;
cmd.cmd_type = MMC_BOOT_CMD_BCAST_W_RESP;
cmd.resp_type = MMC_BOOT_RESP_R2;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Response contains card's 128 bits CID register */
mmc_ret = mmc_boot_decode_and_save_cid( card, cmd.resp );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Ask any card to send it's relative card address (RCA).This RCA number is
* shorter than CID and is used by the host to address the card in future (CMD3)
*/
static unsigned int mmc_boot_send_relative_address( struct mmc_boot_card* card )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD3 Format:
* [31:0] stuff bits
*/
if(card->type == MMC_BOOT_TYPE_SDHC || card->type == MMC_BOOT_TYPE_STD_SD)
{
cmd.cmd_index = CMD3_SEND_RELATIVE_ADDR;
cmd.argument = 0;
cmd.cmd_type = MMC_BOOT_CMD_BCAST_W_RESP;
cmd.resp_type = MMC_BOOT_RESP_R6;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* For sD, card will send RCA. Store it */
card->rca = (cmd.resp[0] >> 16);
}
else
{
cmd.cmd_index = CMD3_SEND_RELATIVE_ADDR;
cmd.argument = (MMC_RCA << 16);
card->rca = (cmd.argument >> 16);
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Requests card to send it's CSD register's contents. (CMD9)
*/
static unsigned int mmc_boot_send_csd( struct mmc_boot_card* card )
{
struct mmc_boot_command cmd;
unsigned int mmc_arg = 0;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD9 Format:
* [31:16] RCA
* [15:0] stuff bits
*/
mmc_arg |= card->rca << 16;
cmd.cmd_index = CMD9_SEND_CSD;
cmd.argument = mmc_arg;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R2;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Response contains card's 128 bits CSD register */
/* Decode and save the register */
mmc_ret = mmc_boot_decode_and_save_csd( card, cmd.resp );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Selects a card by sending CMD7 to the card with its RCA.
* If RCA field is set as 0 ( or any other address ),
* the card will be de-selected. (CMD7)
*/
static unsigned int mmc_boot_select_card( struct mmc_boot_card* card,
unsigned int rca )
{
struct mmc_boot_command cmd;
unsigned int mmc_arg = 0;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD7 Format:
* [31:16] RCA
* [15:0] stuff bits
*/
mmc_arg |= rca << 16;
cmd.cmd_index = CMD7_SELECT_DESELECT_CARD;
cmd.argument = mmc_arg;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
/* If we are deselecting card, we do not get response */
if( rca == card->rca && rca)
{
if(card->type == MMC_BOOT_TYPE_SDHC || card->type == MMC_BOOT_TYPE_STD_SD)
cmd.resp_type = MMC_BOOT_RESP_R1B;
else
cmd.resp_type = MMC_BOOT_RESP_R1;
}
else
{
cmd.resp_type = MMC_BOOT_RESP_NONE;
}
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* As of now no need to look into a response. If it's required
* we'll explore later on */
return MMC_BOOT_E_SUCCESS;
}
/*
* Send command to set block length.
*/
static unsigned int mmc_boot_set_block_len( struct mmc_boot_card* card,
unsigned int block_len )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD16 Format:
* [31:0] block length
*/
cmd.cmd_index = CMD16_SET_BLOCKLEN;
cmd.argument = block_len;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* If blocklength is larger than 512 bytes,
* the card sets BLOCK_LEN_ERROR bit. */
if( cmd.resp[0] & MMC_BOOT_R1_BLOCK_LEN_ERR )
{
return MMC_BOOT_E_BLOCKLEN_ERR;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Requests the card to stop transmission of data.
*/
static unsigned int mmc_boot_send_stop_transmission( struct mmc_boot_card* card,
unsigned int prg_enabled )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD12 Format:
* [31:0] stuff bits
*/
cmd.cmd_index = CMD12_STOP_TRANSMISSION;
cmd.argument = 0;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1B;
cmd.xfer_mode = MMC_BOOT_XFER_MODE_BLOCK;
cmd.prg_enabled = prg_enabled;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Get the card's current status
*/
static unsigned int mmc_boot_get_card_status( struct mmc_boot_card* card,
unsigned int prg_enabled, unsigned int* status )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD13 Format:
* [31:16] RCA
* [15:0] stuff bits
*/
cmd.cmd_index = CMD13_SEND_STATUS;
cmd.argument = card->rca << 16;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
cmd.prg_enabled = prg_enabled;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Checking ADDR_OUT_OF_RANGE error in CMD13 response */
if(IS_ADDR_OUT_OF_RANGE(cmd.resp[0]))
{
return MMC_BOOT_E_FAILURE;
}
*status = cmd.resp[0];
return MMC_BOOT_E_SUCCESS;
}
/*
* Decode type of error caused during read and write
*/
static unsigned int mmc_boot_status_error(unsigned mmc_status)
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* If DATA_CRC_FAIL bit is set to 1 then CRC error was detected by
card/device during the data transfer */
if( mmc_status & MMC_BOOT_MCI_STAT_DATA_CRC_FAIL )
{
mmc_ret = MMC_BOOT_E_DATA_CRC_FAIL;
}
/* If DATA_TIMEOUT bit is set to 1 then the data transfer time exceeded
the data timeout period without completing the transfer */
else if( mmc_status & MMC_BOOT_MCI_STAT_DATA_TIMEOUT )
{
mmc_ret = MMC_BOOT_E_DATA_TIMEOUT;
}
/* If RX_OVERRUN bit is set to 1 then SDCC2 tried to receive data from
the card before empty storage for new received data was available.
Verify that bit FLOW_ENA in MCI_CLK is set to 1 during the data xfer.*/
else if( mmc_status & MMC_BOOT_MCI_STAT_RX_OVRRUN )
{
/* Note: We've set FLOW_ENA bit in MCI_CLK to 1. so no need to verify
for now */
mmc_ret = MMC_BOOT_E_RX_OVRRUN;
}
/* If TX_UNDERRUN bit is set to 1 then SDCC2 tried to send data to
the card before new data for sending was available. Verify that bit
FLOW_ENA in MCI_CLK is set to 1 during the data xfer.*/
else if( mmc_status & MMC_BOOT_MCI_STAT_TX_UNDRUN )
{
/* Note: We've set FLOW_ENA bit in MCI_CLK to 1.so skipping it now*/
mmc_ret = MMC_BOOT_E_RX_OVRRUN;
}
return mmc_ret;
}
/*
* Send ext csd command.
*/
static unsigned int mmc_boot_send_ext_cmd (struct mmc_boot_card* card, unsigned char* buf)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_reg = 0;
unsigned int mmc_status = 0;
unsigned int* mmc_ptr = (unsigned int *)buf;
unsigned int mmc_count = 0;
unsigned int read_error;
memset(buf,0, 512);
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
/* set block len */
if( (card->type != MMC_BOOT_TYPE_MMCHC) && (card->type != MMC_BOOT_TYPE_SDHC) )
{
mmc_ret = mmc_boot_set_block_len( card, 512);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure setting block length for Card (RCA:%s)\n",
mmc_ret, (char *)(card->rca) );
return mmc_ret;
}
}
/* Set the FLOW_ENA bit of MCI_CLK register to 1 */
mmc_reg = readl( MMC_BOOT_MCI_CLK );
mmc_reg |= MMC_BOOT_MCI_CLK_ENA_FLOW ;
writel( mmc_reg, MMC_BOOT_MCI_CLK );
/* Write data timeout period to MCI_DATA_TIMER register. */
/* Data timeout period should be in card bus clock periods */
mmc_reg =0xFFFFFFFF;
writel( mmc_reg, MMC_BOOT_MCI_DATA_TIMER );
writel( 512, MMC_BOOT_MCI_DATA_LENGTH );
/* Set appropriate fields and write the MCI_DATA_CTL register. */
/* Set ENABLE bit to 1 to enable the data transfer. */
mmc_reg = MMC_BOOT_MCI_DATA_ENABLE | MMC_BOOT_MCI_DATA_DIR | (512 << MMC_BOOT_MCI_BLKSIZE_POS);
writel( mmc_reg, MMC_BOOT_MCI_DATA_CTL );
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD8 */
cmd.cmd_index = CMD8_SEND_EXT_CSD;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
cmd.xfer_mode = MMC_BOOT_XFER_MODE_BLOCK;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
MMC_BOOT_MCI_STAT_RX_OVRRUN;
/* Read the transfer data from SDCC2 FIFO. If Data Mover is not used
read the data from the MCI_FIFO register as long as RXDATA_AVLBL
bit of MCI_STATUS register is set to 1 and bits DATA_CRC_FAIL,
DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS register are cleared to 0.
Continue the reads until the whole transfer data is received */
do
{
mmc_ret = MMC_BOOT_E_SUCCESS;
mmc_status = readl( MMC_BOOT_MCI_STATUS );
if( mmc_status & read_error )
{
mmc_ret = mmc_boot_status_error(mmc_status);
break;
}
if( mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL )
{
unsigned read_count = 1;
if ( mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL)
{
read_count = MMC_BOOT_MCI_HFIFO_COUNT;
}
for (int i=0; i<read_count; i++)
{
/* FIFO contains 16 32-bit data buffer on 16 sequential addresses*/
*mmc_ptr = readl( MMC_BOOT_MCI_FIFO +
( mmc_count % MMC_BOOT_MCI_FIFO_SIZE ) );
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof( unsigned int );
}
/* quit if we have read enough of data */
if (mmc_count >= 512)
break;
}
else if( mmc_status & MMC_BOOT_MCI_STAT_DATA_END )
{
break;
}
}while(1);
return MMC_BOOT_E_SUCCESS;
}
/*
* Switch command
*/
static unsigned int mmc_boot_switch_cmd (struct mmc_boot_card* card,
unsigned access,
unsigned index,
unsigned value)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD6 Format:
* [31:26] set to 0
* [25:24] access
* [23:16] index
* [15:8] value
* [7:3] set to 0
* [2:0] cmd set
*/
cmd.cmd_index = CMD6_SWITCH_FUNC;
cmd.argument |= (access << 24);
cmd.argument |= (index << 16);
cmd.argument |= (value << 8);
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1B;
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* A command to set the data bus width for card. Set width to either
*/
static unsigned int mmc_boot_set_bus_width( struct mmc_boot_card* card,
unsigned int width )
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_reg = 0;
unsigned int mmc_width = 0;
unsigned int status;
if( width != MMC_BOOT_BUS_WIDTH_1_BIT)
{
mmc_width = width-1;
}
do
{
mmc_ret = mmc_boot_get_card_status(card, 1, &status);
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
}while(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_PROG_STATE);
if(MMC_BOOT_CARD_STATUS(status) != MMC_BOOT_TRAN_STATE)
return MMC_BOOT_E_FAILURE;
mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE, MMC_BOOT_EXT_CMMC_BUS_WIDTH, mmc_width);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* set MCI_CLK accordingly */
mmc_reg = readl( MMC_BOOT_MCI_CLK );
mmc_reg &= ~MMC_BOOT_MCI_CLK_WIDEBUS_MODE;
if ( width == MMC_BOOT_BUS_WIDTH_1_BIT )
{
mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT;
}
else if (width == MMC_BOOT_BUS_WIDTH_4_BIT )
{
mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT;
}
else if (width == MMC_BOOT_BUS_WIDTH_8_BIT )
{
mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT;
}
writel( mmc_reg, MMC_BOOT_MCI_CLK );
mdelay(10); // Giving some time to card to stabilize.
return MMC_BOOT_E_SUCCESS;
}
/*
* A command to start data read from card. Either a single block or
* multiple blocks can be read. Multiple blocks read will continuously
* transfer data from card to host unless requested to stop by issuing
* CMD12 - STOP_TRANSMISSION.
*/
static unsigned int mmc_boot_send_read_command( struct mmc_boot_card* card,
unsigned int xfer_type,
unsigned int data_addr )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD17/18 Format:
* [31:0] Data Address
*/
if( xfer_type == MMC_BOOT_XFER_MULTI_BLOCK )
{
cmd.cmd_index = CMD18_READ_MULTIPLE_BLOCK;
}
else
{
cmd.cmd_index = CMD17_READ_SINGLE_BLOCK;
}
cmd.argument = data_addr;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Response contains 32 bit Card status. Here we'll check
BLOCK_LEN_ERROR and ADDRESS_ERROR */
if( cmd.resp[0] & MMC_BOOT_R1_BLOCK_LEN_ERR )
{
return MMC_BOOT_E_BLOCKLEN_ERR;
}
/* Misaligned address not matching block length */
if( cmd.resp[0] & MMC_BOOT_R1_ADDR_ERR )
{
return MMC_BOOT_E_ADDRESS_ERR;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* A command to start data write to card. Either a single block or
* multiple blocks can be written. Multiple block write will continuously
* transfer data from host to card unless requested to stop by issuing
* CMD12 - STOP_TRANSMISSION.
*/
static unsigned int mmc_boot_send_write_command( struct mmc_boot_card* card,
unsigned int xfer_type,
unsigned int data_addr )
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* basic check */
if( card == NULL )
{
return MMC_BOOT_E_INVAL;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* CMD24/25 Format:
* [31:0] Data Address
*/
if( xfer_type == MMC_BOOT_XFER_MULTI_BLOCK )
{
cmd.cmd_index = CMD25_WRITE_MULTIPLE_BLOCK;
}
else
{
cmd.cmd_index = CMD24_WRITE_SINGLE_BLOCK;
}
cmd.argument = data_addr;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Response contains 32 bit Card status. Here we'll check
BLOCK_LEN_ERROR and ADDRESS_ERROR */
if( cmd.resp[0] & MMC_BOOT_R1_BLOCK_LEN_ERR )
{
return MMC_BOOT_E_BLOCKLEN_ERR;
}
/* Misaligned address not matching block length */
if( cmd.resp[0] & MMC_BOOT_R1_ADDR_ERR )
{
return MMC_BOOT_E_ADDRESS_ERR;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Write data_len data to address specified by data_addr. data_len is
* multiple of blocks for block data transfer.
*/
static unsigned int mmc_boot_write_to_card( struct mmc_boot_host* host,
struct mmc_boot_card* card,
unsigned long long data_addr,
unsigned int data_len,
unsigned int* in )
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_status = 0;
unsigned int* mmc_ptr = in;
unsigned int mmc_count = 0;
unsigned int mmc_reg = 0;
unsigned int addr;
unsigned int xfer_type;
unsigned int write_error;
unsigned int status;
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
/* Set block length. High Capacity MMC/SD card uses fixed 512 bytes block
length. So no need to send CMD16. */
if( (card->type != MMC_BOOT_TYPE_MMCHC) && (card->type != MMC_BOOT_TYPE_SDHC) )
{
mmc_ret = mmc_boot_set_block_len( card, card->wr_block_len );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure setting block length for Card\
(RCA:%s)\n", mmc_ret, (char *)(card->rca) );
return mmc_ret;
}
}
/* use multi-block mode to transfer for data larger than a block */
xfer_type = (data_len > card->rd_block_len) ? MMC_BOOT_XFER_MULTI_BLOCK :
MMC_BOOT_XFER_SINGLE_BLOCK;
/* For MMCHC/SDHC data address is specified in unit of 512B */
addr = ( (card->type != MMC_BOOT_TYPE_MMCHC) && (card->type != MMC_BOOT_TYPE_SDHC) )
? (unsigned int) data_addr : (unsigned int) (data_addr / 512);
/* Set the FLOW_ENA bit of MCI_CLK register to 1 */
mmc_reg = readl( MMC_BOOT_MCI_CLK );
mmc_reg |= MMC_BOOT_MCI_CLK_ENA_FLOW ;
writel( mmc_reg, MMC_BOOT_MCI_CLK );
/* Write data timeout period to MCI_DATA_TIMER register */
/* Data timeout period should be in card bus clock periods */
/*TODO: Fix timeout value*/
mmc_reg = 0xFFFFFFFF;
writel( mmc_reg, MMC_BOOT_MCI_DATA_TIMER );
/* Write the total size of the transfer data to MCI_DATA_LENGTH register */
writel( data_len, MMC_BOOT_MCI_DATA_LENGTH );
/* Send command to the card/device in order to start the write data xfer.
The possible commands are CMD24/25/53/60/61 */
mmc_ret = mmc_boot_send_write_command( card, xfer_type, addr );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure sending write command to the\
Card(RCA:%x)\n", mmc_ret, card->rca );
return mmc_ret;
}
/* Set appropriate fields and write the MCI_DATA_CTL register */
/* Set ENABLE bit to 1 to enable the data transfer. */
mmc_reg = 0;
mmc_reg |= MMC_BOOT_MCI_DATA_ENABLE;
/* Clear DIRECTION bit to 0 to enable transfer from host to card */
/* Clear MODE bit to 0 to enable block oriented data transfer. For
MMC cards only, if stream data transfer mode is desired, set
MODE bit to 1. */
/* Set DM_ENABLE bit to 1 in order to enable DMA, otherwise set 0 */
/* Write size of block to be used during the data transfer to
BLOCKSIZE field */
mmc_reg |= card->wr_block_len << MMC_BOOT_MCI_BLKSIZE_POS;
writel( mmc_reg, MMC_BOOT_MCI_DATA_CTL );
write_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
MMC_BOOT_MCI_STAT_TX_UNDRUN;
/* Write the transfer data to SDCC3 FIFO */
/* If Data Mover is used for data transfer, prepare a command list entry
and enable the Data Mover to work with SDCC2 */
/* If Data Mover is NOT used for data xfer: */
do
{
mmc_ret = MMC_BOOT_E_SUCCESS;
mmc_status = readl( MMC_BOOT_MCI_STATUS );
if( mmc_status & write_error )
{
mmc_ret = mmc_boot_status_error(mmc_status);
break;
}
/* Write the data in MCI_FIFO register as long as TXFIFO_FULL bit of
MCI_STATUS register is 0. Continue the writes until the whole
transfer data is written. */
if (((data_len-mmc_count) >= MMC_BOOT_MCI_FIFO_SIZE/2) &&
( mmc_status & MMC_BOOT_MCI_STAT_TX_FIFO_HFULL ))
{
for (int i=0; i < MMC_BOOT_MCI_HFIFO_COUNT; i++ )
{
/* FIFO contains 16 32-bit data buffer on 16 sequential addresses*/
writel( *mmc_ptr, MMC_BOOT_MCI_FIFO +
( mmc_count % MMC_BOOT_MCI_FIFO_SIZE ) );
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof( unsigned int );
}
}
else if( !( mmc_status & MMC_BOOT_MCI_STAT_TX_FIFO_FULL ) && (mmc_count != data_len))
{
/* FIFO contains 16 32-bit data buffer on 16 sequential addresses*/
writel( *mmc_ptr, MMC_BOOT_MCI_FIFO +
( mmc_count % MMC_BOOT_MCI_FIFO_SIZE ) );
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof( unsigned int );
}
else if((mmc_status & MMC_BOOT_MCI_STAT_DATA_END))
{
break; //success
}
} while(1);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure on data transfer from the \
Card(RCA:%x)\n", mmc_ret, card->rca );
/* In case of any failure happening for multi block transfer */
if( xfer_type == MMC_BOOT_XFER_MULTI_BLOCK )
mmc_boot_send_stop_transmission( card, 1 );
return mmc_ret;
}
/* Send command to the card/device in order to poll the de-assertion of
card/device BUSY condition. It is important to set PROG_ENA bit in
MCI_CLK register before sending the command. Possible commands are
CMD12/13. */
if( xfer_type == MMC_BOOT_XFER_MULTI_BLOCK )
{
mmc_ret = mmc_boot_send_stop_transmission( card, 1 );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure sending Stop Transmission \
command to the Card(RCA:%x)\n", mmc_ret, card->rca );
return mmc_ret;
}
}
else
{
mmc_ret = mmc_boot_get_card_status( card, 1, &status );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure getting card status of Card(RCA:%x)\n",
mmc_ret, card->rca );
return mmc_ret;
}
}
/* Wait for interrupt or poll on PROG_DONE bit of MCI_STATUS register. If
PROG_DONE bit is set to 1 it means that the card finished it programming
and stopped driving DAT0 line to 0 */
do
{
mmc_status = readl( MMC_BOOT_MCI_STATUS );
if( mmc_status & MMC_BOOT_MCI_STAT_PROG_DONE )
{
break;
}
} while(1);
return MMC_BOOT_E_SUCCESS;
}
/*
* Adjust the interface speed to optimal speed
*/
static unsigned int mmc_boot_adjust_interface_speed( struct mmc_boot_host* host,
struct mmc_boot_card* card )
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int status;
do
{
mmc_ret = mmc_boot_get_card_status(card, 1, &status);
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
}while(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_PROG_STATE);
if(MMC_BOOT_CARD_STATUS(status) != MMC_BOOT_TRAN_STATE)
return MMC_BOOT_E_FAILURE;
/* Setting HS_TIMING in EXT_CSD (CMD6) */
mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE, MMC_BOOT_EXT_CMMC_HS_TIMING, 1);
if(mmc_ret!= MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
#ifdef PLATFORM_MSM8X60
mmc_ret = mmc_boot_enable_clock( host, MMC_CLK_48MHZ);
#else
mmc_ret = mmc_boot_enable_clock( host, MMC_CLK_50MHZ);
#endif
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return MMC_BOOT_E_CLK_ENABLE_FAIL;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Reads a data of data_len from the address specified. data_len
* should be multiple of block size for block data transfer.
*/
static unsigned int mmc_boot_read_from_card( struct mmc_boot_host* host,
struct mmc_boot_card* card,
unsigned long long data_addr,
unsigned int data_len,
unsigned int* out )
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_status = 0;
unsigned int* mmc_ptr = out;
unsigned int mmc_count = 0;
unsigned int mmc_reg = 0;
unsigned int xfer_type;
unsigned int addr = 0;
unsigned int read_error;
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
/* Set block length. High Capacity MMC/SD card uses fixed 512 bytes block
length. So no need to send CMD16. */
if( (card->type != MMC_BOOT_TYPE_MMCHC) && (card->type != MMC_BOOT_TYPE_SDHC) )
{
mmc_ret = mmc_boot_set_block_len( card, card->rd_block_len );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure setting block length for Card (RCA:%s)\n",
mmc_ret, (char *)(card->rca) );
return mmc_ret;
}
}
/* use multi-block mode to transfer for data larger than a block */
xfer_type = (data_len > card->rd_block_len) ? MMC_BOOT_XFER_MULTI_BLOCK :
MMC_BOOT_XFER_SINGLE_BLOCK;
/* Set the FLOW_ENA bit of MCI_CLK register to 1 */
/* Note: It's already enabled */
/* If Data Mover is used for data transfer then prepare Command
List Entry and enable the Data mover to work with SDCC2 */
/* Note: Data Mover not used */
/* Write data timeout period to MCI_DATA_TIMER register. */
/* Data timeout period should be in card bus clock periods */
mmc_reg = (unsigned long)(card->rd_timeout_ns / 1000000) *
(host->mclk_rate / 1000);
mmc_reg += 1000; // add some extra clock cycles to be safe
mmc_reg = mmc_reg/2;
writel( mmc_reg, MMC_BOOT_MCI_DATA_TIMER );
/* Write the total size of the transfer data to MCI_DATA_LENGTH
register. For block xfer it must be multiple of the block
size. */
writel( data_len, MMC_BOOT_MCI_DATA_LENGTH );
/* For MMCHC/SDHC data address is specified in unit of 512B */
addr = ( (card->type != MMC_BOOT_TYPE_MMCHC) && (card->type != MMC_BOOT_TYPE_SDHC) )
? (unsigned int) data_addr :(unsigned int) (data_addr / 512);
/* Set appropriate fields and write the MCI_DATA_CTL register. */
/* Set ENABLE bit to 1 to enable the data transfer. */
mmc_reg = 0;
mmc_reg |= MMC_BOOT_MCI_DATA_ENABLE;
/* Clear DIRECTION bit to 1 to enable transfer from card to host */
mmc_reg |= MMC_BOOT_MCI_DATA_DIR;
/* Clear MODE bit to 0 to enable block oriented data transfer. For
MMC cards only, if stream data transfer mode is desired, set
MODE bit to 1. */
/* Set DM_ENABLE bit to 1 in order to enable DMA, otherwise set 0 */
/* Write size of block to be used during the data transfer to
BLOCKSIZE field */
mmc_reg |= (card->rd_block_len << MMC_BOOT_MCI_BLKSIZE_POS);
writel( mmc_reg, MMC_BOOT_MCI_DATA_CTL );
/* Send command to the card/device in order to start the read data
transfer. Possible commands: CMD17/18/53/60/61. */
mmc_ret = mmc_boot_send_read_command( card, xfer_type, addr );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure sending read command to the Card(RCA:%x)\n",
mmc_ret, card->rca );
return mmc_ret;
}
read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
MMC_BOOT_MCI_STAT_RX_OVRRUN;
/* Read the transfer data from SDCC2 FIFO. If Data Mover is not used
read the data from the MCI_FIFO register as long as RXDATA_AVLBL
bit of MCI_STATUS register is set to 1 and bits DATA_CRC_FAIL,
DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS register are cleared to 0.
Continue the reads until the whole transfer data is received */
do
{
mmc_ret = MMC_BOOT_E_SUCCESS;
mmc_status = readl( MMC_BOOT_MCI_STATUS );
if( mmc_status & read_error )
{
mmc_ret = mmc_boot_status_error(mmc_status);
break;
}
if( mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL )
{
unsigned read_count = 1;
if ( mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL)
{
read_count = MMC_BOOT_MCI_HFIFO_COUNT;
}
for (int i=0; i<read_count; i++)
{
/* FIFO contains 16 32-bit data buffer on 16 sequential addresses*/
*mmc_ptr = readl( MMC_BOOT_MCI_FIFO +
( mmc_count % MMC_BOOT_MCI_FIFO_SIZE ) );
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof( unsigned int );
}
/* quit if we have read enough of data */
if (mmc_count == data_len)
break;
}
else if( mmc_status & MMC_BOOT_MCI_STAT_DATA_END )
{
break;
}
}while(1);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure on data transfer from the \
Card(RCA:%x)\n", mmc_ret, card->rca );
return mmc_ret;
}
/* In case a multiple block transfer was performed, send CMD12 to the
card/device in order to indicate the end of read data transfer */
if( xfer_type == MMC_BOOT_XFER_MULTI_BLOCK )
{
mmc_ret = mmc_boot_send_stop_transmission( card, 0 );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure sending Stop Transmission \
command to the Card(RCA:%x)\n", mmc_ret, card->rca );
return mmc_ret;
}
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Initialize host structure, set and enable clock-rate and power mode.
*/
unsigned int mmc_boot_init( struct mmc_boot_host* host )
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_pwr = 0;
host->ocr = MMC_BOOT_OCR_27_36 | MMC_BOOT_OCR_SEC_MODE;
host->cmd_retry = MMC_BOOT_MAX_COMMAND_RETRY;
host->clk_enabled = 0;
/* clock frequency should be less than 400KHz in identification mode */
mmc_ret = mmc_boot_enable_clock( host, MMC_CLK_400KHZ);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return MMC_BOOT_E_CLK_ENABLE_FAIL;
}
/* set power mode*/
/* give some time to reach minimum voltate */
mdelay(2);
mmc_pwr &= ~MMC_BOOT_MCI_PWR_UP;
mmc_pwr |= MMC_BOOT_MCI_PWR_ON;
mmc_pwr |= MMC_BOOT_MCI_PWR_UP;
writel( mmc_pwr, MMC_BOOT_MCI_POWER );
/* some more time to stabilize voltage */
mdelay(2);
return MMC_BOOT_E_SUCCESS;
}
/*
* Performs card identification process by getting card's unique identification
* number (CID) and relative card address (RCA). After that card will be in
* stand-by state.
*/
static unsigned int mmc_boot_identify_card( struct mmc_boot_host* host,
struct mmc_boot_card* card)
{
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
/* basic check */
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
/* Ask card to send its unique card identification (CID) number (CMD2) */
mmc_return = mmc_boot_all_send_cid( card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No. %d: Failure getting card's CID number!\n",
mmc_return );
return mmc_return;
}
/* Ask card to send a relative card address (RCA) (CMD3) */
mmc_return = mmc_boot_send_relative_address( card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No. %d: Failure getting card's RCA!\n",
mmc_return );
return mmc_return;
}
/* Set the card status as active */
card->status = MMC_BOOT_STATUS_ACTIVE;
/* Get card's CSD register (CMD9) */
mmc_return = mmc_boot_send_csd( card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure getting card's CSD information!\n",
mmc_return );
return mmc_return;
}
/* Once CSD is received, set read and write timeout value now itself */
mmc_return = mmc_boot_set_read_timeout( host, card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure setting Read Timeout value!\n",
mmc_return );
return mmc_return;
}
mmc_return = mmc_boot_set_write_timeout( host, card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure setting Write Timeout value!\n",
mmc_return );
return mmc_return;
}
return MMC_BOOT_E_SUCCESS;
}
static unsigned int mmc_boot_send_app_cmd(unsigned int rca)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
cmd.cmd_index = CMD55_APP_CMD;
cmd.argument = (rca << 16);
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
mmc_ret = mmc_boot_send_command(&cmd);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
static unsigned int mmc_boot_sd_init_card(struct mmc_boot_card* card)
{
unsigned int i,mmc_ret;
unsigned int ocr_cmd_arg;
struct mmc_boot_command cmd;
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* Send CMD8 to set interface condition */
for(i=0;i<3;i++)
{
cmd.cmd_index = CMD8_SEND_IF_COND;
cmd.argument = MMC_BOOT_SD_HC_VOLT_SUPPLIED;
cmd.cmd_type = MMC_BOOT_CMD_BCAST_W_RESP;
cmd.resp_type = MMC_BOOT_RESP_R7;
mmc_ret = mmc_boot_send_command(&cmd);
if( mmc_ret == MMC_BOOT_E_SUCCESS )
{
if(cmd.resp[0] != MMC_BOOT_SD_HC_VOLT_SUPPLIED)
return MMC_BOOT_E_FAILURE;
/* Set argument for ACMD41 */
ocr_cmd_arg = MMC_BOOT_SD_NEG_OCR | MMC_BOOT_SD_HC_HCS;
break;
}
mdelay(1);
}
/* Send ACMD41 to set operating condition */
/* Try for a max of 1 sec as per spec */
for(i=0;i<20;i++)
{
mmc_ret = mmc_boot_send_app_cmd(0);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
cmd.cmd_index = ACMD41_SEND_OP_COND;
cmd.argument = ocr_cmd_arg;
cmd.cmd_type = MMC_BOOT_CMD_BCAST_W_RESP;
cmd.resp_type = MMC_BOOT_RESP_R3;
mmc_ret = mmc_boot_send_command(&cmd);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
else if (cmd.resp[0] & MMC_BOOT_SD_DEV_READY)
{
/* Check for HC */
if(cmd.resp[0] & (1 << 30))
{
card->type = MMC_BOOT_TYPE_SDHC;
}
else
{
card->type = MMC_BOOT_TYPE_STD_SD;
}
break;
}
mdelay(50);
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Routine to initialize MMC card. It resets a card to idle state, verify operating
* voltage and set the card inready state.
*/
static unsigned int mmc_boot_init_card( struct mmc_boot_host* host,
struct mmc_boot_card* card )
{
unsigned int mmc_retry = 0;
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
/* basic check */
if( ( host == NULL ) || ( card == NULL ) )
{
return MMC_BOOT_E_INVAL;
}
/* 1. Card Reset - CMD0 */
mmc_return = mmc_boot_reset_cards();
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.:%d: Failure resetting MMC cards!\n", mmc_return);
return mmc_return;
}
/* 2. Card Initialization process */
/* Send CMD1 to identify and reject cards that do not match host's VDD range
profile. Cards sends its OCR register in response.
*/
mmc_retry = 0;
do
{
mmc_return = mmc_boot_send_op_cond( host, card );
/* Card returns busy status. We'll retry again! */
if( mmc_return == MMC_BOOT_E_CARD_BUSY )
{
mmc_retry++;
mdelay(200);
continue;
}
else if( mmc_return == MMC_BOOT_E_SUCCESS )
{
break;
}
else
{
dprintf(CRITICAL, "Error No. %d: Failure Initializing MMC Card!\n",
mmc_return );
/* Check for sD card */
mmc_return = mmc_boot_sd_init_card(card);
return mmc_return;
}
}while( mmc_retry < host->cmd_retry );
/* If card still returned busy status we are out of luck.
* Card cannot be initialized */
if( mmc_return == MMC_BOOT_E_CARD_BUSY )
{
dprintf(CRITICAL, "Error No. %d: Card has busy status set. \
Initialization not completed\n", mmc_return );
return MMC_BOOT_E_CARD_BUSY;
}
return MMC_BOOT_E_SUCCESS;
}
static unsigned int mmc_boot_set_sd_bus_width(struct mmc_boot_card* card, unsigned int width)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int sd_reg;
mmc_ret = mmc_boot_send_app_cmd(card->rca);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* Send ACMD6 to set bus width */
cmd.cmd_index = ACMD6_SET_BUS_WIDTH;
/* 10 => 4 bit wide */
cmd.argument = (1<<1);
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
mmc_ret = mmc_boot_send_command(&cmd);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* set MCI_CLK accordingly */
sd_reg = readl( MMC_BOOT_MCI_CLK );
sd_reg &= ~MMC_BOOT_MCI_CLK_WIDEBUS_MODE;
if ( width == MMC_BOOT_BUS_WIDTH_1_BIT )
{
sd_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT;
}
else if (width == MMC_BOOT_BUS_WIDTH_4_BIT )
{
sd_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT;
}
else if (width == MMC_BOOT_BUS_WIDTH_8_BIT )
{
sd_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT;
}
writel( sd_reg, MMC_BOOT_MCI_CLK );
mdelay(10); // Giving some time to card to stabilize.
return MMC_BOOT_E_SUCCESS;
}
static unsigned int mmc_boot_set_sd_hs(struct mmc_boot_host* host, struct mmc_boot_card* card)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret;
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* Send CMD6 function mode 1 to set high speed */
/* Not using mode 0 to read current consumption */
cmd.cmd_index = CMD6_SWITCH_FUNC;
cmd.argument = MMC_BOOT_SD_SWITCH_HS;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
mmc_ret = mmc_boot_send_command(&cmd);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
mdelay(1);
#ifdef PLATFORM_MSM8X60
mmc_ret = mmc_boot_enable_clock( host, MMC_CLK_48MHZ);
#else
mmc_ret = mmc_boot_enable_clock( host, MMC_CLK_50MHZ);
#endif
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return MMC_BOOT_E_CLK_ENABLE_FAIL;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Performs initialization and identification of all the MMC cards connected
* to the host.
*/
static unsigned int mmc_boot_init_and_identify_cards( struct mmc_boot_host* host, struct mmc_boot_card* card )
{
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
unsigned int status;
/* Basic check */
if( host == NULL )
{
return MMC_BOOT_E_INVAL;
}
/* Initialize MMC card structure */
card->status = MMC_BOOT_STATUS_INACTIVE;
card->rd_block_len = MMC_BOOT_RD_BLOCK_LEN;
card->wr_block_len = MMC_BOOT_WR_BLOCK_LEN;
/* Start initialization process (CMD0 & CMD1) */
mmc_return = mmc_boot_init_card( host, card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
return mmc_return;
}
/* Start card identification process (CMD2, CMD3 & CMD9)*/
mmc_return = mmc_boot_identify_card( host, card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
return mmc_return;
}
/* Select the card (CMD7) */
mmc_return = mmc_boot_select_card( card, card->rca );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure selecting the Card with RCA: %x\n",
mmc_return, card->rca );
return mmc_return;
}
if(card->type == MMC_BOOT_TYPE_SDHC || card->type == MMC_BOOT_TYPE_STD_SD)
{
mmc_return = mmc_boot_set_sd_hs(host, card);
if(mmc_return != MMC_BOOT_E_SUCCESS)
{
return mmc_return;
}
mmc_return = mmc_boot_set_sd_bus_width(card, MMC_BOOT_BUS_WIDTH_4_BIT);
if(mmc_return != MMC_BOOT_E_SUCCESS)
{
return mmc_return;
}
}
else
{
/* set interface speed */
mmc_return = mmc_boot_adjust_interface_speed( host, card );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Error adjusting interface speed!\n",
mmc_return );
return mmc_return;
}
/* enable wide bus */
mmc_return = mmc_boot_set_bus_width( card, MMC_BOOT_BUS_WIDTH_4_BIT );
if( mmc_return != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure to set wide bus for Card(RCA:%x)\n",
mmc_return, card->rca );
return mmc_return;
}
}
/* Just checking whether we're in TRAN state after changing speed and bus width */
mmc_return = mmc_boot_get_card_status(card, 1, &status);
if(mmc_return != MMC_BOOT_E_SUCCESS)
{
return mmc_return;
}
if(MMC_BOOT_CARD_STATUS(status) != MMC_BOOT_TRAN_STATE)
return MMC_BOOT_E_FAILURE;
return MMC_BOOT_E_SUCCESS;
}
/*
* Read MBR from MMC card and fill partition table.
*/
static unsigned int mmc_boot_read_MBR(void)
{
unsigned char buffer[MMC_BOOT_RD_BLOCK_LEN];
unsigned int dtype;
unsigned int dfirstsec;
unsigned int EBR_first_sec;
unsigned int EBR_current_sec;
int ret = 0;
int idx, i;
/* Print out the MBR first */
ret = mmc_boot_read_from_card( &mmc_host, &mmc_card, 0, \
MMC_BOOT_RD_BLOCK_LEN, \
(unsigned int *)buffer);
if (ret)
{
return ret;
}
/* Check to see if signature exists */
if ((buffer[TABLE_SIGNATURE] != 0x55) || \
(buffer[TABLE_SIGNATURE + 1] != 0xAA))
{
return -1;
}
/* Print out the first 4 partition */
idx = TABLE_ENTRY_0;
for (i = 0; i < 4; i++)
{
mbr[mmc_partition_count].dstatus = \
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
mbr[mmc_partition_count].dtype = \
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
mbr[mmc_partition_count].dfirstsec = \
GET_LWORD_FROM_BYTE(&buffer[idx + \
i * TABLE_ENTRY_SIZE + \
OFFSET_FIRST_SEC]);
mbr[mmc_partition_count].dsize = \
GET_LWORD_FROM_BYTE(&buffer[idx + \
i * TABLE_ENTRY_SIZE + \
OFFSET_SIZE]);
dtype = mbr[mmc_partition_count].dtype;
dfirstsec = mbr[mmc_partition_count].dfirstsec;
mbr_fill_name(&mbr[mmc_partition_count], \
mbr[mmc_partition_count].dtype);
mmc_partition_count++;
if (mmc_partition_count == MAX_PARTITIONS)
return ret;
}
/* See if the last partition is EBR, if not, parsing is done */
if (dtype != 0x05)
{
return ret;
}
EBR_first_sec = dfirstsec;
EBR_current_sec = dfirstsec;
ret = mmc_boot_read_from_card( &mmc_host, &mmc_card, \
(EBR_first_sec * 512), \
MMC_BOOT_RD_BLOCK_LEN, \
(unsigned int *)buffer);
if (ret)
{
return ret;
}
/* Loop to parse the EBR */
for (i = 0;; i++)
{
if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA))
{
break;
}
mbr[mmc_partition_count].dstatus = \
buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
mbr[mmc_partition_count].dtype = \
buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
mbr[mmc_partition_count].dfirstsec = \
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
OFFSET_FIRST_SEC]) + \
EBR_current_sec;
mbr[mmc_partition_count].dsize = \
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
OFFSET_SIZE]);
mbr_fill_name(&(mbr[mmc_partition_count]), \
mbr[mmc_partition_count].dtype);
mmc_partition_count++;
if (mmc_partition_count == MAX_PARTITIONS)
return ret;
dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
if(dfirstsec == 0)
{
/* Getting to the end of the EBR tables */
break;
}
/* More EBR to follow - read in the next EBR sector */
ret = mmc_boot_read_from_card( &mmc_host, &mmc_card, \
((EBR_first_sec + dfirstsec) * 512), \
MMC_BOOT_RD_BLOCK_LEN, \
(unsigned int *)buffer);
if (ret)
{
return ret;
}
EBR_current_sec = EBR_first_sec + dfirstsec;
}
return ret;
}
/*
* Entry point to MMC boot process
*/
unsigned int mmc_boot_main(unsigned char slot, unsigned int base)
{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
memset( (struct mmc_boot_host*)&mmc_host, 0, sizeof( struct mmc_boot_host ) );
memset( (struct mmc_boot_card*)&mmc_card, 0, sizeof(struct mmc_boot_card) );
mmc_slot = slot;
mmc_boot_mci_base = base;
#ifndef PLATFORM_MSM8X60
/* Waiting for modem to come up */
while (readl(MSM_SHARED_BASE + 0x14) != 1);
#endif
/* Initialize necessary data structure and enable/set clock and power */
dprintf(INFO," Initializing MMC host data structure and clock!\n" );
mmc_ret = mmc_boot_init( &mmc_host );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "MMC Boot: Error Initializing MMC Card!!!\n" );
return MMC_BOOT_E_FAILURE;
}
/* Initialize and identify cards connected to host */
mmc_ret = mmc_boot_init_and_identify_cards( &mmc_host, &mmc_card );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "MMC Boot: Failure detecting MMC card!!!\n" );
return MMC_BOOT_E_FAILURE;
}
mmc_display_csd();
mmc_display_ext_csd();
/* Read MBR of the card */
mmc_ret = mmc_boot_read_MBR();
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "MMC Boot: MBR read failed!\n" );
return MMC_BOOT_E_FAILURE;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* MMC write function
*/
unsigned int mmc_write (unsigned long long data_addr, unsigned int data_len, unsigned int* in)
{
int val = 0;
unsigned int write_size = ((unsigned)(0xFFFFFF/512))*512;
unsigned offset = 0;
unsigned int *sptr = in;
if(data_len % 512)
data_len = ROUND_TO_PAGE(data_len, 511);
while(data_len > write_size)
{
val = mmc_boot_write_to_card( &mmc_host, &mmc_card, \
data_addr + offset, \
write_size, sptr);
if(val)
{
return val;
}
sptr += (write_size/sizeof(unsigned));
offset += write_size;
data_len -= write_size;
}
if (data_len)
{
val = mmc_boot_write_to_card( &mmc_host, &mmc_card, \
data_addr + offset, \
data_len, sptr);
}
return val;
}
/*
* MMC read function
*/
unsigned int mmc_read (unsigned long long data_addr, unsigned int* out, unsigned int data_len)
{
int val = 0;
val = mmc_boot_read_from_card( &mmc_host, &mmc_card, data_addr, data_len, out);
return val;
}
/*
* Fill name for android partition found.
*/
static void mbr_fill_name (struct mbr_entry *mbr_ent, unsigned int type)
{
switch(type)
{
memset(mbr_ent->name, 0, 64);
case MMC_MODEM_TYPE:
case MMC_MODEM_TYPE2:
/* if there are more than one with type "modem", mmc_ptn_offset will return the first one */
memcpy(mbr_ent->name,"modem",5);
break;
case MMC_SBL1_TYPE:
memcpy(mbr_ent->name,"sbl1",4);
break;
case MMC_SBL2_TYPE:
memcpy(mbr_ent->name,"sbl2",4);
break;
case MMC_SBL3_TYPE:
memcpy(mbr_ent->name,"sbl3",4);
break;
case MMC_RPM_TYPE:
memcpy(mbr_ent->name,"rpm",3);
break;
case MMC_TZ_TYPE:
memcpy(mbr_ent->name,"tz",2);
break;
case MMC_ABOOT_TYPE:
memcpy(mbr_ent->name,"aboot",5);
break;
case MMC_BOOT_TYPE:
memcpy(mbr_ent->name,"boot",4);
break;
case MMC_MODEM_ST1_TYPE:
memcpy(mbr_ent->name,"modem_st1",9);
break;
case MMC_MODEM_ST2_TYPE:
memcpy(mbr_ent->name,"modem_st2",9);
break;
case MMC_EFS2_TYPE:
memcpy(mbr_ent->name,"efs2",4);
break;
case MMC_USERDATA_TYPE:
strcpy((char *)mbr_ent->name,(const char *)ext3_partitions[ext3_count]);
ext3_count++;
break;
case MMC_RECOVERY_TYPE:
memcpy(mbr_ent->name,"recovery",8);
break;
};
}
/*
* Returns offset of given partition
*/
unsigned long long mmc_ptn_offset (unsigned char * name)
{
unsigned n;
for(n = 0; n < mmc_partition_count; n++) {
if(!strcmp((const char *)mbr[n].name, (const char *)name)) {
return (mbr[n].dfirstsec * MMC_BOOT_RD_BLOCK_LEN);
}
}
return 0;
}
unsigned long long mmc_ptn_size (unsigned char * name)
{
unsigned n;
for(n = 0; n < mmc_partition_count; n++) {
if(!strcmp((const char *)mbr[n].name, (const char *)name)) {
return (mbr[n].dsize * MMC_BOOT_RD_BLOCK_LEN);
}
}
return 0;
}
/*
* Function to read registers from MMC or SD card
*/
static unsigned int mmc_boot_read_reg(struct mmc_boot_card *card,
unsigned int data_len,
unsigned int command, unsigned int addr,
unsigned int *out)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_status = 0;
unsigned int* mmc_ptr = out;
unsigned int mmc_count = 0;
unsigned int mmc_reg = 0;
unsigned int xfer_type;
unsigned int read_error;
/* Set the FLOW_ENA bit of MCI_CLK register to 1 */
mmc_reg = readl( MMC_BOOT_MCI_CLK );
mmc_reg |= MMC_BOOT_MCI_CLK_ENA_FLOW ;
writel( mmc_reg, MMC_BOOT_MCI_CLK );
/* Write data timeout period to MCI_DATA_TIMER register. */
/* Data timeout period should be in card bus clock periods */
mmc_reg =0xFFFFFFFF;
writel( mmc_reg, MMC_BOOT_MCI_DATA_TIMER );
writel( data_len, MMC_BOOT_MCI_DATA_LENGTH );
/* Set appropriate fields and write the MCI_DATA_CTL register. */
/* Set ENABLE bit to 1 to enable the data transfer. */
mmc_reg = MMC_BOOT_MCI_DATA_ENABLE | MMC_BOOT_MCI_DATA_DIR | (data_len << MMC_BOOT_MCI_BLKSIZE_POS);
writel( mmc_reg, MMC_BOOT_MCI_DATA_CTL );
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
cmd.cmd_index = command;
cmd.argument = addr;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1;
/* send command */
mmc_ret = mmc_boot_send_command( &cmd );
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
MMC_BOOT_MCI_STAT_RX_OVRRUN;
do
{
mmc_ret = MMC_BOOT_E_SUCCESS;
mmc_status = readl( MMC_BOOT_MCI_STATUS );
if( mmc_status & read_error )
{
mmc_ret = mmc_boot_status_error(mmc_status);
break;
}
if( mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL )
{
unsigned read_count = 1;
if ( mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL)
{
read_count = MMC_BOOT_MCI_HFIFO_COUNT;
}
for (int i=0; i<read_count; i++)
{
/* FIFO contains 16 32-bit data buffer on 16 sequential addresses*/
*mmc_ptr = readl( MMC_BOOT_MCI_FIFO +
( mmc_count % MMC_BOOT_MCI_FIFO_SIZE ) );
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof( unsigned int );
}
/* quit if we have read enough of data */
if (mmc_count == data_len)
break;
}
else if( mmc_status & MMC_BOOT_MCI_STAT_DATA_END )
{
break;
}
}while(1);
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
dprintf(CRITICAL, "Error No.%d: Failure on data transfer from the \
Card(RCA:%x)\n", mmc_ret, card->rca );
return mmc_ret;
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Function to set/clear power-on write protection for the user area partitions
*/
static unsigned int mmc_boot_set_clr_power_on_wp_user(struct mmc_boot_card* card,
unsigned int addr,
unsigned int size,
unsigned char set_clear_wp)
{
struct mmc_boot_command cmd;
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int wp_group_size, loop_count;
unsigned int status;
memset( (struct mmc_boot_command *)&cmd, 0,
sizeof(struct mmc_boot_command) );
/* Disabling PERM_WP for USER AREA (CMD6) */
mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE,
MMC_BOOT_EXT_USER_WP,
MMC_BOOT_US_PERM_WP_DIS);
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
/* Sending CMD13 to check card status */
do
{
mmc_ret = mmc_boot_get_card_status( card, 0 ,&status);
if(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_TRAN_STATE)
break;
} while( (mmc_ret == MMC_BOOT_E_SUCCESS) &&
(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_PROG_STATE));
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
mmc_ret = mmc_boot_send_ext_cmd (card,ext_csd_buf);
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
/* Make sure power-on write protection for user area is not disabled
and permanent write protection for user area is not enabled */
if((IS_BIT_SET_EXT_CSD(MMC_BOOT_EXT_USER_WP, MMC_BOOT_US_PERM_WP_EN)) ||
(IS_BIT_SET_EXT_CSD(MMC_BOOT_EXT_USER_WP, MMC_BOOT_US_PWR_WP_DIS)))
{
return MMC_BOOT_E_FAILURE;
}
if(ext_csd_buf[MMC_BOOT_EXT_ERASE_GROUP_DEF])
{
/* wp_group_size = 512KB * HC_WP_GRP_SIZE * HC_ERASE_GRP_SIZE.
Getting write protect group size in sectors here. */
wp_group_size = (512*1024) * ext_csd_buf[MMC_BOOT_EXT_HC_WP_GRP_SIZE] *
ext_csd_buf[MMC_BOOT_EXT_HC_ERASE_GRP_SIZE] /
MMC_BOOT_WR_BLOCK_LEN;
}
else
{
/* wp_group_size = (WP_GRP_SIZE + 1) * (ERASE_GRP_SIZE + 1)
* (ERASE_GRP_MULT + 1).
This is defined as the number of write blocks directly */
wp_group_size = (card->csd.erase_grp_size + 1) *
(card->csd.erase_grp_mult + 1) *
(card->csd.wp_grp_size + 1);
}
if(wp_group_size == 0)
{
return MMC_BOOT_E_FAILURE;
}
/* Setting POWER_ON_WP for USER AREA (CMD6) */
mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE,
MMC_BOOT_EXT_USER_WP,
MMC_BOOT_US_PWR_WP_EN);
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
/* Sending CMD13 to check card status */
do
{
mmc_ret = mmc_boot_get_card_status( card, 0 ,&status);
if(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_TRAN_STATE)
break;
} while( (mmc_ret == MMC_BOOT_E_SUCCESS) &&
(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_PROG_STATE));
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
/* Calculating the loop count for sending SET_WRITE_PROTECT (CMD28)
or CLEAR_WRITE_PROTECT (CMD29).
We are write protecting the partitions in blocks of write protect
group sizes only */
if(size % wp_group_size)
{
loop_count = (size / wp_group_size) + 1;
}
else
{
loop_count = (size / wp_group_size);
}
if(set_clear_wp)
cmd.cmd_index = CMD28_SET_WRITE_PROTECT;
else
cmd.cmd_index = CMD29_CLEAR_WRITE_PROTECT;
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
cmd.resp_type = MMC_BOOT_RESP_R1B;
for(int i=0;i<loop_count;i++)
{
/* Sending CMD28 for each WP group size
address is in sectors already */
cmd.argument = (addr + (i * wp_group_size));
mmc_ret = mmc_boot_send_command( &cmd );
if(mmc_ret != MMC_BOOT_E_SUCCESS)
{
return mmc_ret;
}
/* Checking ADDR_OUT_OF_RANGE error in CMD28 response */
if(IS_ADDR_OUT_OF_RANGE(cmd.resp[0]))
{
return MMC_BOOT_E_FAILURE;
}
/* Sending CMD13 to check card status */
do
{
mmc_ret = mmc_boot_get_card_status( card, 0 ,&status);
if(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_TRAN_STATE)
break;
} while( (mmc_ret == MMC_BOOT_E_SUCCESS) &&
(MMC_BOOT_CARD_STATUS(status) == MMC_BOOT_PROG_STATE));
if( mmc_ret != MMC_BOOT_E_SUCCESS )
{
return mmc_ret;
}
}
return MMC_BOOT_E_SUCCESS;
}
/*
* Function to get Write Protect status of the given sector
*/
static unsigned int mmc_boot_get_wp_status (struct mmc_boot_card* card,
unsigned int sector)
{
unsigned int rc = MMC_BOOT_E_SUCCESS;
memset(wp_status_buf,0, 8);
rc = mmc_boot_read_reg(card,8,CMD31_SEND_WRITE_PROT_TYPE,sector,wp_status_buf);
return rc;
}
/*
* Test Function for setting Write protect for given sector
*/
static unsigned int mmc_wp(unsigned int sector, unsigned int size,
unsigned char set_clear_wp)
{
unsigned int rc = MMC_BOOT_E_SUCCESS;
/* Checking whether group write protection feature is available */
if(mmc_card.csd.wp_grp_enable)
{
rc = mmc_boot_get_wp_status(&mmc_card,sector);
rc = mmc_boot_set_clr_power_on_wp_user(&mmc_card,sector,size,set_clear_wp);
rc = mmc_boot_get_wp_status(&mmc_card,sector);
return rc;
}
else
return MMC_BOOT_E_FAILURE;
}
void mmc_wp_test(void)
{
unsigned int mmc_ret=0;
mmc_ret = mmc_wp(0xE06000,0x5000,1);
}
void mmc_display_ext_csd(void)
{
dprintf(ALWAYS, "part_config: %x\n", ext_csd_buf[179] );
dprintf(ALWAYS, "erase_group_def: %x\n", ext_csd_buf[175] );
dprintf(ALWAYS, "user_wp: %x\n", ext_csd_buf[171] );
}
void mmc_display_csd(void)
{
dprintf(ALWAYS, "erase_grpsize: %d\n", mmc_card.csd.erase_grp_size );
dprintf(ALWAYS, "erase_grpmult: %d\n", mmc_card.csd.erase_grp_mult );
dprintf(ALWAYS, "wp_grpsize: %d\n", mmc_card.csd.wp_grp_size );
dprintf(ALWAYS, "wp_grpen: %d\n", mmc_card.csd.wp_grp_enable );
dprintf(ALWAYS, "perm_wp: %d\n", mmc_card.csd.perm_wp );
dprintf(ALWAYS, "temp_wp: %d\n", mmc_card.csd.temp_wp );
}