3408 lines
91 KiB
C
3408 lines
91 KiB
C
|
/* Copyright (c) 2010-2013, The Linux Foundation. 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 The Linux Foundation 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 <partition_parser.h>
|
||
|
#include <platform/iomap.h>
|
||
|
#include <platform/timer.h>
|
||
|
#include <bits.h>
|
||
|
|
||
|
#if MMC_BOOT_ADM
|
||
|
#include "adm.h"
|
||
|
#endif
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
#include "bam.h"
|
||
|
#include "mmc_dml.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef NULL
|
||
|
#define NULL 0
|
||
|
#endif
|
||
|
|
||
|
#define USEC_PER_SEC (1000000L)
|
||
|
|
||
|
#define MMC_BOOT_DATA_READ 0
|
||
|
#define MMC_BOOT_DATA_WRITE 1
|
||
|
|
||
|
static unsigned int mmc_boot_data_transfer(unsigned int *data_ptr,
|
||
|
unsigned int data_len,
|
||
|
unsigned char direction);
|
||
|
|
||
|
static unsigned int mmc_boot_fifo_read(unsigned int *data_ptr,
|
||
|
unsigned int data_len);
|
||
|
|
||
|
static unsigned int mmc_boot_fifo_write(unsigned int *data_ptr,
|
||
|
unsigned int data_len);
|
||
|
|
||
|
static unsigned int mmc_boot_status_error(unsigned mmc_status);
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
|
||
|
void mmc_boot_dml_init();
|
||
|
|
||
|
static void mmc_boot_dml_producer_trans_init(unsigned trans_end,
|
||
|
unsigned size);
|
||
|
|
||
|
static void mmc_boot_dml_consumer_trans_init();
|
||
|
|
||
|
static uint32_t mmc_boot_dml_chk_producer_idle();
|
||
|
|
||
|
static void mmc_boot_dml_wait_producer_idle();
|
||
|
static void mmc_boot_dml_wait_consumer_idle();
|
||
|
static void mmc_boot_dml_reset();
|
||
|
static int mmc_bam_init(uint32_t bam_base);
|
||
|
static int mmc_bam_transfer_data();
|
||
|
static unsigned int
|
||
|
mmc_boot_bam_setup_desc(unsigned int *data_ptr,
|
||
|
unsigned int data_len, unsigned char direction);
|
||
|
|
||
|
|
||
|
#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 };
|
||
|
|
||
|
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];
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
|
||
|
static uint32_t mmc_sdc_bam_base[] =
|
||
|
{ MSM_SDC1_BAM_BASE, MSM_SDC2_BAM_BASE, MSM_SDC3_BAM_BASE, MSM_SDC4_BAM_BASE };
|
||
|
|
||
|
static uint32_t mmc_sdc_dml_base[] =
|
||
|
{ MSM_SDC1_DML_BASE, MSM_SDC2_DML_BASE, MSM_SDC3_DML_BASE, MSM_SDC4_DML_BASE };
|
||
|
|
||
|
uint32_t dml_base;
|
||
|
static struct bam_instance bam;
|
||
|
|
||
|
#define MMC_BOOT_BAM_FIFO_SIZE 100
|
||
|
|
||
|
#define MMC_BOOT_BAM_READ_PIPE_INDEX 0
|
||
|
#define MMC_BOOT_BAM_WRITE_PIPE_INDEX 1
|
||
|
|
||
|
#define MMC_BOOT_BAM_READ_PIPE 0
|
||
|
#define MMC_BOOT_BAM_WRITE_PIPE 1
|
||
|
|
||
|
/* Align at BAM_DESC_SIZE boundary */
|
||
|
static struct bam_desc desc_fifo[MMC_BOOT_BAM_FIFO_SIZE] __attribute__ ((aligned(BAM_DESC_SIZE)));
|
||
|
|
||
|
#endif
|
||
|
|
||
|
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);
|
||
|
|
||
|
struct mmc_host mmc_host;
|
||
|
struct mmc_card mmc_card;
|
||
|
|
||
|
static unsigned int mmc_wp(unsigned int addr, unsigned int size,
|
||
|
unsigned char set_clear_wp);
|
||
|
static unsigned int mmc_boot_send_ext_cmd(struct mmc_card *card,
|
||
|
unsigned char *buf);
|
||
|
static unsigned int mmc_boot_read_reg(struct mmc_card *card,
|
||
|
unsigned int data_len,
|
||
|
unsigned int command,
|
||
|
unsigned int addr, unsigned int *out);
|
||
|
|
||
|
unsigned int SWAP_ENDIAN(unsigned int val)
|
||
|
{
|
||
|
return ((val & 0xFF) << 24) |
|
||
|
(((val >> 8) & 0xFF) << 16) | (((val >> 16) & 0xFF) << 8) | (val >>
|
||
|
24);
|
||
|
}
|
||
|
|
||
|
void mmc_mclk_reg_wr_delay()
|
||
|
{
|
||
|
if (mmc_host.mmc_cont_version)
|
||
|
{
|
||
|
/* Wait for the MMC_BOOT_MCI register write to go through. */
|
||
|
while(readl(MMC_BOOT_MCI_STATUS2) & MMC_BOOT_MCI_MCLK_REG_WR_ACTIVE);
|
||
|
}
|
||
|
else
|
||
|
udelay((1 + ((3 * USEC_PER_SEC) / (mmc_host.mclk_rate? mmc_host.mclk_rate : MMC_CLK_400KHZ))));
|
||
|
}
|
||
|
|
||
|
/* Sets a timeout for read operation.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_set_read_timeout(struct mmc_host *host,
|
||
|
struct mmc_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(SPEW, " 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_host *host,
|
||
|
struct mmc_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(SPEW, " 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_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_csd mmc_csd;
|
||
|
|
||
|
if ((card == NULL) || (raw_csd == NULL)) {
|
||
|
return MMC_BOOT_E_INVAL;
|
||
|
}
|
||
|
|
||
|
mmc_sizeof = sizeof(unsigned int) * 8;
|
||
|
|
||
|
mmc_csd.cmmc_structure = UNPACK_BITS(raw_csd, 126, 2, mmc_sizeof);
|
||
|
|
||
|
if ((card->type == MMC_BOOT_TYPE_SDHC)
|
||
|
|| (card->type == MMC_BOOT_TYPE_STD_SD)) {
|
||
|
/* Parse CSD according to SD card spec. */
|
||
|
|
||
|
/* 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 */
|
||
|
|
||
|
if (mmc_csd.cmmc_structure == 1) {
|
||
|
/* CSD Version 2.0 */
|
||
|
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 = 0x2; /* Fixed value: 010b */
|
||
|
mmc_csd.c_size_mult = 0; /* not there in version 2.0 */
|
||
|
mmc_csd.c_size =
|
||
|
UNPACK_BITS(raw_csd, 48, 22, mmc_sizeof);
|
||
|
mmc_csd.nsac_clk_cycle =
|
||
|
UNPACK_BITS(raw_csd, 104, 8, mmc_sizeof) * 100;
|
||
|
|
||
|
//TODO: Investigate the nsac and taac. Spec suggests not using this for timeouts.
|
||
|
|
||
|
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;
|
||
|
|
||
|
mmc_csd.wp_grp_size = 0x0;
|
||
|
mmc_csd.wp_grp_enable = 0x0;
|
||
|
mmc_csd.perm_wp =
|
||
|
UNPACK_BITS(raw_csd, 13, 1, mmc_sizeof);
|
||
|
mmc_csd.temp_wp =
|
||
|
UNPACK_BITS(raw_csd, 12, 1, mmc_sizeof);
|
||
|
|
||
|
/* Calculate the card capcity */
|
||
|
card->capacity = (1 + mmc_csd.c_size) * 512 * 1024;
|
||
|
} else {
|
||
|
/* CSD Version 1.0 */
|
||
|
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_csd.wp_grp_size =
|
||
|
UNPACK_BITS(raw_csd, 32, 7, 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);
|
||
|
|
||
|
/* Calculate the card capacity */
|
||
|
mmc_temp =
|
||
|
(1 << (mmc_csd.c_size_mult + 2)) * (mmc_csd.c_size +
|
||
|
1);
|
||
|
card->capacity = mmc_temp * mmc_csd.read_blk_len;
|
||
|
}
|
||
|
} else {
|
||
|
/* Parse CSD according to MMC card spec. */
|
||
|
mmc_csd.spec_vers = UNPACK_BITS(raw_csd, 122, 4, mmc_sizeof);
|
||
|
mmc_csd.card_cmd_class =
|
||
|
UNPACK_BITS(raw_csd, 84, 12, mmc_sizeof);
|
||
|
mmc_csd.write_blk_len =
|
||
|
1 << UNPACK_BITS(raw_csd, 22, 4, mmc_sizeof);
|
||
|
mmc_csd.read_blk_len =
|
||
|
1 << UNPACK_BITS(raw_csd, 80, 4, mmc_sizeof);
|
||
|
mmc_csd.r2w_factor = UNPACK_BITS(raw_csd, 26, 3, 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_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.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.tran_speed = 0x00; /* Ignore -- no use of this value. */
|
||
|
|
||
|
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);
|
||
|
|
||
|
/* Calculate the card capcity */
|
||
|
if (mmc_csd.c_size != 0xFFF) {
|
||
|
/* For cards less than or equal to 2GB */
|
||
|
mmc_temp =
|
||
|
(1 << (mmc_csd.c_size_mult + 2)) * (mmc_csd.c_size +
|
||
|
1);
|
||
|
card->capacity = mmc_temp * mmc_csd.read_blk_len;
|
||
|
} else {
|
||
|
/* For cards greater than 2GB, Ext CSD register's SEC_COUNT
|
||
|
* is used to calculate the size.
|
||
|
*/
|
||
|
unsigned long long sec_count;
|
||
|
|
||
|
sec_count = (ext_csd_buf[215] << 24) |
|
||
|
(ext_csd_buf[214] << 16) |
|
||
|
(ext_csd_buf[213] << 8) | ext_csd_buf[212];
|
||
|
|
||
|
card->capacity = sec_count * 512;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* save the information in card structure */
|
||
|
memcpy((struct mmc_csd *)&card->csd,
|
||
|
(struct mmc_csd *)&mmc_csd, sizeof(struct mmc_csd));
|
||
|
|
||
|
dprintf(SPEW, "Decoded CSD fields:\n");
|
||
|
dprintf(SPEW, "cmmc_structure: %d\n", mmc_csd.cmmc_structure);
|
||
|
dprintf(SPEW, "card_cmd_class: %x\n", mmc_csd.card_cmd_class);
|
||
|
dprintf(SPEW, "write_blk_len: %d\n", mmc_csd.write_blk_len);
|
||
|
dprintf(SPEW, "read_blk_len: %d\n", mmc_csd.read_blk_len);
|
||
|
dprintf(SPEW, "r2w_factor: %d\n", mmc_csd.r2w_factor);
|
||
|
dprintf(SPEW, "sector_size: %d\n", mmc_csd.sector_size);
|
||
|
dprintf(SPEW, "c_size_mult:%d\n", mmc_csd.c_size_mult);
|
||
|
dprintf(SPEW, "c_size: %d\n", mmc_csd.c_size);
|
||
|
dprintf(SPEW, "nsac_clk_cycle: %d\n", mmc_csd.nsac_clk_cycle);
|
||
|
dprintf(SPEW, "taac_ns: %d\n", mmc_csd.taac_ns);
|
||
|
dprintf(SPEW, "tran_speed: %d kbps\n", mmc_csd.tran_speed);
|
||
|
dprintf(SPEW, "erase_blk_len: %d\n", mmc_csd.erase_blk_len);
|
||
|
dprintf(SPEW, "read_blk_misalign: %d\n", mmc_csd.read_blk_misalign);
|
||
|
dprintf(SPEW, "write_blk_misalign: %d\n", mmc_csd.write_blk_misalign);
|
||
|
dprintf(SPEW, "read_blk_partial: %d\n", mmc_csd.read_blk_partial);
|
||
|
dprintf(SPEW, "write_blk_partial: %d\n", mmc_csd.write_blk_partial);
|
||
|
dprintf(SPEW, "Card Capacity: %llu 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_card *card, unsigned int *raw_cid)
|
||
|
{
|
||
|
struct mmc_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;
|
||
|
|
||
|
if ((card->type == MMC_BOOT_TYPE_SDHC)
|
||
|
|| (card->type == MMC_BOOT_TYPE_STD_SD)) {
|
||
|
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 < 5; i++) {
|
||
|
mmc_cid.pnm[i] = (unsigned char)UNPACK_BITS(raw_cid,
|
||
|
(104 -
|
||
|
8 * (i +
|
||
|
1)),
|
||
|
8,
|
||
|
mmc_sizeof);
|
||
|
}
|
||
|
mmc_cid.pnm[5] = 0;
|
||
|
mmc_cid.pnm[6] = 0;
|
||
|
|
||
|
mmc_cid.prv = UNPACK_BITS(raw_cid, 56, 8, mmc_sizeof);
|
||
|
mmc_cid.psn = UNPACK_BITS(raw_cid, 24, 32, mmc_sizeof);
|
||
|
mmc_cid.month = UNPACK_BITS(raw_cid, 8, 4, mmc_sizeof);
|
||
|
mmc_cid.year = UNPACK_BITS(raw_cid, 12, 8, mmc_sizeof);
|
||
|
mmc_cid.year += 2000;
|
||
|
} else {
|
||
|
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, 8, 4, mmc_sizeof);
|
||
|
mmc_cid.year = UNPACK_BITS(raw_cid, 12, 4, mmc_sizeof);
|
||
|
mmc_cid.year += 1997;
|
||
|
}
|
||
|
|
||
|
/* save it in card database */
|
||
|
memcpy((struct mmc_cid *)&card->cid,
|
||
|
(struct mmc_cid *)&mmc_cid, sizeof(struct mmc_cid));
|
||
|
|
||
|
dprintf(SPEW, "Decoded CID fields:\n");
|
||
|
dprintf(SPEW, "Manufacturer ID: %x\n", mmc_cid.mid);
|
||
|
dprintf(SPEW, "OEM ID: 0x%x\n", mmc_cid.oid);
|
||
|
dprintf(SPEW, "Product Name: %s\n", mmc_cid.pnm);
|
||
|
dprintf(SPEW, "Product revision: %d.%d\n", (mmc_cid.prv >> 4),
|
||
|
(mmc_cid.prv & 0xF));
|
||
|
dprintf(SPEW, "Product serial number: %X\n", mmc_cid.psn);
|
||
|
dprintf(SPEW, "Manufacturing date: %d %d\n", mmc_cid.month,
|
||
|
mmc_cid.year);
|
||
|
|
||
|
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 */
|
||
|
if (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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CMD write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
dprintf(SPEW, "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(SPEW, "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);
|
||
|
|
||
|
|
||
|
/* 2k. Write to MMC_BOOT_MCI_CMD register */
|
||
|
writel(0, MMC_BOOT_MCI_CMD);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CMD write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
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_host *host, struct mmc_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_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_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_card *card, unsigned int *raw_csd)
|
||
|
{
|
||
|
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 the card csd */
|
||
|
memcpy(raw_csd, cmd.resp, sizeof(cmd.resp));
|
||
|
|
||
|
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_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_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_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_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_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_ptr = (unsigned int *)buf;
|
||
|
|
||
|
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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
#if MMC_BOOT_ADM || MMC_BOOT_BAM
|
||
|
mmc_reg |= MMC_BOOT_MCI_DATA_DM_ENABLE;
|
||
|
#endif
|
||
|
|
||
|
writel(mmc_reg, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
/* Setup SDCC BAM descriptors for Read operation. */
|
||
|
mmc_ret = mmc_boot_bam_setup_desc(mmc_ptr, 512, MMC_BOOT_DATA_READ);
|
||
|
#endif
|
||
|
|
||
|
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 the transfer data from SDCC FIFO. */
|
||
|
mmc_ret = mmc_boot_data_transfer(mmc_ptr, 512, MMC_BOOT_DATA_READ);
|
||
|
|
||
|
/* Reset DPSM */
|
||
|
writel(0, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Switch command
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_switch_cmd(struct mmc_card *card,
|
||
|
unsigned access, unsigned index, unsigned value)
|
||
|
{
|
||
|
|
||
|
struct mmc_boot_command cmd;
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
uint32_t mmc_status;
|
||
|
|
||
|
/* 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;
|
||
|
cmd.prg_enabled = 1;
|
||
|
|
||
|
mmc_ret = mmc_boot_send_command(&cmd);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,"Send cmd6 failed\n");
|
||
|
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);
|
||
|
|
||
|
/* Check if the card completed the switch command processing */
|
||
|
mmc_ret = mmc_boot_get_card_status(card, 0, &mmc_status);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL, "Get card status failed\n");
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
if (MMC_BOOT_CARD_STATUS(mmc_status) != MMC_BOOT_TRAN_STATE)
|
||
|
{
|
||
|
dprintf(CRITICAL, "Switch cmd failed. Card not in tran state\n");
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (mmc_status & MMC_BOOT_SWITCH_FUNC_ERR_FLAG)
|
||
|
{
|
||
|
dprintf(CRITICAL, "Switch cmd failed. Switch Error.\n");
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* A command to set the data bus width for card. Set width to either
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_set_bus_width(struct mmc_card *card, unsigned int width)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int mmc_reg = 0;
|
||
|
unsigned int mmc_width = 0;
|
||
|
|
||
|
if (width != MMC_BOOT_BUS_WIDTH_1_BIT) {
|
||
|
mmc_width = width - 1;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
dprintf(CRITICAL, "Switch cmd failed\n");
|
||
|
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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to enable HS200 mode
|
||
|
* 1. Set the clock frequency to 100 MHZ
|
||
|
* 2. Set the bus width to 4/8 bit SDR as supported byt the target & host
|
||
|
* 3. Set the HS_TIMING on ext_csd 185 for the card
|
||
|
*/
|
||
|
static uint32_t mmc_set_hs200_mode(struct mmc_host *host,
|
||
|
struct mmc_card *card)
|
||
|
{
|
||
|
uint32_t mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
/* Set Clock @ 100 MHZ */
|
||
|
clock_config_mmc(mmc_slot, host->caps.hs_clk_rate);
|
||
|
host->mclk_rate = host->caps.hs_clk_rate;
|
||
|
|
||
|
/* Set 4/8 bit SDR bus width */
|
||
|
mmc_ret = mmc_boot_set_bus_width(card, host->caps.bus_width);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure to set wide bus for Card(RCA:%x)\n",
|
||
|
mmc_ret, card->rca);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/* Setting HS200 in HS_TIMING using EXT_CSD (CMD6) */
|
||
|
mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE,
|
||
|
MMC_BOOT_EXT_CMMC_HS_TIMING, 2);
|
||
|
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL, "Switch cmd returned failure %d\n", __LINE__);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to enable DDR mode
|
||
|
* 1. Set the bus width to 8 bit DDR
|
||
|
* 1. Set the clock frequency to 100 MHZ
|
||
|
* 3. Set DDR mode in mci clk register
|
||
|
* 4. Set Widebus enable in mci clock register
|
||
|
*/
|
||
|
static uint32_t mmc_set_ddr_mode(struct mmc_host *host,
|
||
|
struct mmc_card *card)
|
||
|
{
|
||
|
uint8_t mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
uint32_t mmc_reg = 0;
|
||
|
uint32_t width = 0;
|
||
|
|
||
|
switch (host->caps.bus_width) {
|
||
|
case MMC_BOOT_BUS_WIDTH_4_BIT:
|
||
|
width = MMC_DDR_BUS_WIDTH_4_BIT;
|
||
|
break;
|
||
|
case MMC_BOOT_BUS_WIDTH_8_BIT:
|
||
|
width = MMC_DDR_BUS_WIDTH_8_BIT;
|
||
|
break;
|
||
|
default:
|
||
|
dprintf(CRITICAL, "Invalid bus width, DDR mode is not enabled\n");
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
goto end;
|
||
|
};
|
||
|
|
||
|
/* Set width for 4/8 bit DDR bus width */
|
||
|
mmc_ret = mmc_boot_set_bus_width(card, width);
|
||
|
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure to set wide bus for Card(RCA:%x)\n",
|
||
|
mmc_ret, card->rca);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/* Bump up the clock frequency */
|
||
|
clock_config_mmc(mmc_slot, host->caps.hs_clk_rate);
|
||
|
host->mclk_rate = host->caps.hs_clk_rate;
|
||
|
|
||
|
/* Select DDR mode in mci register */
|
||
|
mmc_reg = readl(MMC_BOOT_MCI_CLK);
|
||
|
mmc_reg |= (MMC_MCI_DDR_MODE_EN << MMC_MCI_MODE_SELECT);
|
||
|
|
||
|
writel(mmc_reg, MMC_BOOT_MCI_CLK);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* Enable wide bus for DDR */
|
||
|
mmc_reg = readl(MMC_BOOT_MCI_CLK);
|
||
|
mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_MODE;
|
||
|
writel(mmc_reg, MMC_BOOT_MCI_CLK);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
end:
|
||
|
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_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_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_host *host,
|
||
|
struct mmc_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_reg = 0;
|
||
|
unsigned int addr;
|
||
|
unsigned int xfer_type;
|
||
|
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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
mmc_boot_bam_setup_desc(in, data_len, MMC_BOOT_DATA_WRITE);
|
||
|
#endif
|
||
|
|
||
|
/* 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 */
|
||
|
|
||
|
#if MMC_BOOT_ADM || MMC_BOOT_BAM
|
||
|
mmc_reg |= MMC_BOOT_MCI_DATA_DM_ENABLE;
|
||
|
#endif
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* write data to FIFO */
|
||
|
mmc_ret =
|
||
|
mmc_boot_data_transfer(in, data_len, MMC_BOOT_DATA_WRITE);
|
||
|
|
||
|
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);
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
/* Wait for DML trasaction to end */
|
||
|
mmc_boot_dml_wait_consumer_idle();
|
||
|
#endif
|
||
|
|
||
|
/* Reset DPSM */
|
||
|
writel(0, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Adjust the interface speed to optimal speed
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_adjust_interface_speed(struct mmc_host *host,
|
||
|
struct mmc_card *card)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
/* 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) {
|
||
|
dprintf(CRITICAL, "Switch cmd returned failure %d\n", __LINE__);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
clock_config_mmc(mmc_slot, MMC_CLK_50MHZ);
|
||
|
|
||
|
host->mclk_rate = MMC_CLK_50MHZ;
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static unsigned int
|
||
|
mmc_boot_set_block_count(struct mmc_card *card, unsigned int block_count)
|
||
|
{
|
||
|
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));
|
||
|
|
||
|
/* CMD23 Format:
|
||
|
* [15:0] number of blocks
|
||
|
*/
|
||
|
|
||
|
cmd.cmd_index = CMD23_SET_BLOCK_COUNT;
|
||
|
cmd.argument = block_count;
|
||
|
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 (cmd.resp[0] & MMC_BOOT_R1_OUT_OF_RANGE) {
|
||
|
return MMC_BOOT_E_BLOCKLEN_ERR;
|
||
|
}
|
||
|
|
||
|
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_host *host,
|
||
|
struct mmc_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_reg = 0;
|
||
|
unsigned int xfer_type;
|
||
|
unsigned int addr = 0;
|
||
|
unsigned char open_ended_read = 1;
|
||
|
|
||
|
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;
|
||
|
|
||
|
if (xfer_type == MMC_BOOT_XFER_MULTI_BLOCK) {
|
||
|
if ((card->type == MMC_BOOT_TYPE_MMCHC)
|
||
|
|| (card->type == MMC_BOOT_TYPE_STD_MMC)) {
|
||
|
/* Virtio model does not support open-ended multi-block reads.
|
||
|
* So, block count must be set before sending read command.
|
||
|
* All SD cards do not support this command. Restrict this to MMC.
|
||
|
*/
|
||
|
mmc_ret =
|
||
|
mmc_boot_set_block_count(card,
|
||
|
data_len /
|
||
|
(card->rd_block_len));
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure setting read block count for Card (RCA:%s)\n",
|
||
|
mmc_ret, (char *)(card->rca));
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
open_ended_read = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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 */
|
||
|
|
||
|
/* 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. */
|
||
|
|
||
|
/* If DMA is to be used, Set DM_ENABLE bit to 1 */
|
||
|
|
||
|
#if MMC_BOOT_ADM || MMC_BOOT_BAM
|
||
|
mmc_reg |= MMC_BOOT_MCI_DATA_DM_ENABLE;
|
||
|
#endif
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
/* Setup SDCC FIFO descriptors for Read operation. */
|
||
|
mmc_ret = mmc_boot_bam_setup_desc(out, data_len, MMC_BOOT_DATA_READ);
|
||
|
#endif
|
||
|
/* 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 the transfer data from SDCC FIFO. */
|
||
|
mmc_ret = mmc_boot_data_transfer(out, data_len, MMC_BOOT_DATA_READ);
|
||
|
|
||
|
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) && open_ended_read) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Reset DPSM */
|
||
|
writel(0, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initialize host structure, set and enable clock-rate and power mode.
|
||
|
*/
|
||
|
unsigned int mmc_boot_init(struct mmc_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;
|
||
|
|
||
|
/* Initialize any clocks needed for SDC controller */
|
||
|
clock_init_mmc(mmc_slot);
|
||
|
|
||
|
/* Save the verison on the mmc controller. */
|
||
|
host->mmc_cont_version = readl(MMC_BOOT_MCI_VERSION);
|
||
|
|
||
|
/* Setup initial freq to 400KHz */
|
||
|
clock_config_mmc(mmc_slot, MMC_CLK_400KHZ);
|
||
|
|
||
|
host->mclk_rate = MMC_CLK_400KHZ;
|
||
|
|
||
|
/* set power mode */
|
||
|
/* give some time to reach minimum voltate */
|
||
|
|
||
|
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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_POWER write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Performs card identification process:
|
||
|
* - get card's unique identification number (CID)
|
||
|
* - get(for sd)/set (for mmc) relative card address (RCA)
|
||
|
* - get CSD
|
||
|
* - select the card, thus transitioning it to Transfer State
|
||
|
* - get Extended CSD (for mmc)
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_identify_card(struct mmc_host *host, struct mmc_card *card)
|
||
|
{
|
||
|
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int raw_csd[4];
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* Get card's CSD register (CMD9) */
|
||
|
mmc_return = mmc_boot_send_csd(card, raw_csd);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure getting card's CSD information!\n",
|
||
|
mmc_return);
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* Set the card status as active */
|
||
|
card->status = MMC_BOOT_STATUS_ACTIVE;
|
||
|
|
||
|
if ((card->type == MMC_BOOT_TYPE_STD_MMC)
|
||
|
|| (card->type == MMC_BOOT_TYPE_MMCHC)) {
|
||
|
/* For MMC cards, also get the extended csd */
|
||
|
mmc_return = mmc_boot_send_ext_cmd(card, ext_csd_buf);
|
||
|
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure getting card's ExtCSD information!\n",
|
||
|
mmc_return);
|
||
|
|
||
|
return mmc_return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Decode and save the CSD register */
|
||
|
mmc_return = mmc_boot_decode_and_save_csd(card, raw_csd);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure decoding 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_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_host *host, struct mmc_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(1);
|
||
|
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_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 */
|
||
|
if (width == MMC_BOOT_BUS_WIDTH_1_BIT) {
|
||
|
cmd.argument = 0;
|
||
|
} else if (width == MMC_BOOT_BUS_WIDTH_4_BIT) {
|
||
|
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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static unsigned int
|
||
|
mmc_boot_set_sd_hs(struct mmc_host *host, struct mmc_card *card)
|
||
|
{
|
||
|
unsigned char sw_buf[64];
|
||
|
unsigned int mmc_ret;
|
||
|
|
||
|
/* CMD6 is a data transfer command. sD card returns 512 bits of data */
|
||
|
/* Refer 4.3.10 of sD card specification 3.0 */
|
||
|
mmc_ret =
|
||
|
mmc_boot_read_reg(card, 64, CMD6_SWITCH_FUNC, MMC_BOOT_SD_SWITCH_HS,
|
||
|
(unsigned int *)&sw_buf);
|
||
|
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
clock_config_mmc(mmc_slot, MMC_CLK_50MHZ);
|
||
|
|
||
|
host->mclk_rate = MMC_CLK_50MHZ;
|
||
|
|
||
|
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_host *host,
|
||
|
struct mmc_card *card)
|
||
|
{
|
||
|
unsigned int mmc_return = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int status;
|
||
|
uint8_t mmc_bus_width = 0;
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* Identify (CMD2, CMD3 & CMD9) and select the card (CMD7) */
|
||
|
mmc_return = mmc_boot_identify_card(host, card);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
return mmc_return;
|
||
|
}
|
||
|
|
||
|
if (card->type == MMC_BOOT_TYPE_SDHC
|
||
|
|| card->type == MMC_BOOT_TYPE_STD_SD) {
|
||
|
/* Setting sD card to high speed without checking card's capability.
|
||
|
Cards that do not support high speed may fail to boot */
|
||
|
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) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Couldn't set 4bit mode for sD card\n");
|
||
|
mmc_return =
|
||
|
mmc_boot_set_sd_bus_width(card,
|
||
|
MMC_BOOT_BUS_WIDTH_1_BIT);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failed in setting bus width!\n",
|
||
|
mmc_return);
|
||
|
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 HS200 mode by default if supported,
|
||
|
* else if DDR mode is supported enable it.
|
||
|
* else use default 4/8 bit mode
|
||
|
*/
|
||
|
if (card_supports_hs200_mode() && host->caps.hs200_mode) {
|
||
|
mmc_return = mmc_set_hs200_mode(host, card);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure to set HS200 mode for Card(RCA:%x)\n",
|
||
|
mmc_return, card->rca);
|
||
|
return mmc_return;
|
||
|
}
|
||
|
} else if (card_supports_ddr_mode() && host->caps.ddr_mode) {
|
||
|
mmc_return = mmc_set_ddr_mode(host, card);
|
||
|
if (mmc_return != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error No.%d: Failure to set DDR mode for Card(RCA:%x)\n",
|
||
|
mmc_return, card->rca);
|
||
|
return mmc_return;
|
||
|
}
|
||
|
} else {
|
||
|
mmc_return =
|
||
|
mmc_boot_set_bus_width(card, host->caps.bus_width);
|
||
|
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, 0, &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;
|
||
|
}
|
||
|
|
||
|
void mmc_display_ext_csd(void)
|
||
|
{
|
||
|
dprintf(SPEW, "part_config: %x\n", ext_csd_buf[179]);
|
||
|
dprintf(SPEW, "erase_group_def: %x\n", ext_csd_buf[175]);
|
||
|
dprintf(SPEW, "user_wp: %x\n", ext_csd_buf[171]);
|
||
|
}
|
||
|
|
||
|
void mmc_display_csd(void)
|
||
|
{
|
||
|
dprintf(SPEW, "erase_grpsize: %d\n", mmc_card.csd.erase_grp_size);
|
||
|
dprintf(SPEW, "erase_grpmult: %d\n", mmc_card.csd.erase_grp_mult);
|
||
|
dprintf(SPEW, "wp_grpsize: %d\n", mmc_card.csd.wp_grp_size);
|
||
|
dprintf(SPEW, "wp_grpen: %d\n", mmc_card.csd.wp_grp_enable);
|
||
|
dprintf(SPEW, "perm_wp: %d\n", mmc_card.csd.perm_wp);
|
||
|
dprintf(SPEW, "temp_wp: %d\n", mmc_card.csd.temp_wp);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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_host *)&mmc_host, 0,
|
||
|
sizeof(struct mmc_host));
|
||
|
memset((struct mmc_card *)&mmc_card, 0,
|
||
|
sizeof(struct mmc_card));
|
||
|
|
||
|
mmc_slot = slot;
|
||
|
mmc_boot_mci_base = base;
|
||
|
|
||
|
/* Get the capabilities for the host/target */
|
||
|
target_mmc_caps(&mmc_host);
|
||
|
|
||
|
/* Initialize necessary data structure and enable/set clock and power */
|
||
|
dprintf(SPEW, " 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;
|
||
|
}
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
|
||
|
mmc_ret = mmc_bam_init(mmc_sdc_bam_base[slot - 1]);
|
||
|
dml_base = mmc_sdc_dml_base[slot - 1];
|
||
|
#endif
|
||
|
|
||
|
/* 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: Failed detecting MMC/SDC @ slot%d\n", slot);
|
||
|
return MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
|
||
|
mmc_display_csd();
|
||
|
mmc_display_ext_csd();
|
||
|
|
||
|
mmc_ret = partition_read_table();
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to read registers from MMC or SD card
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_read_reg(struct mmc_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_reg = 0;
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
#if MMC_BOOT_ADM || MMC_BOOT_BAM
|
||
|
mmc_reg |= MMC_BOOT_MCI_DATA_DM_ENABLE;
|
||
|
#endif
|
||
|
|
||
|
writel(mmc_reg, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
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 the transfer data from SDCC FIFO. */
|
||
|
mmc_ret =
|
||
|
mmc_boot_data_transfer(out, data_len, MMC_BOOT_DATA_READ);
|
||
|
|
||
|
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_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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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 (unsigned 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_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,
|
||
|
(unsigned int *)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);
|
||
|
}
|
||
|
|
||
|
unsigned mmc_get_psn(void)
|
||
|
{
|
||
|
return mmc_card.cid.psn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read/write data from/to SDC FIFO.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_data_transfer(unsigned int *data_ptr,
|
||
|
unsigned int data_len, unsigned char direction)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
#if MMC_BOOT_ADM
|
||
|
adm_result_t ret;
|
||
|
adm_dir_t adm_dir;
|
||
|
|
||
|
if (direction == MMC_BOOT_DATA_READ) {
|
||
|
adm_dir = ADM_MMC_READ;
|
||
|
} else {
|
||
|
adm_dir = ADM_MMC_WRITE;
|
||
|
}
|
||
|
|
||
|
ret = adm_transfer_mmc_data(mmc_slot,
|
||
|
(unsigned char *)data_ptr, data_len,
|
||
|
adm_dir);
|
||
|
|
||
|
if (ret != ADM_RESULT_SUCCESS) {
|
||
|
dprintf(CRITICAL, "MMC ADM transfer error: %d\n", ret);
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
|
||
|
#elif MMC_BOOT_BAM
|
||
|
mmc_ret = mmc_bam_transfer_data(data_ptr, data_len, direction);
|
||
|
#else
|
||
|
|
||
|
if (direction == MMC_BOOT_DATA_READ) {
|
||
|
mmc_ret = mmc_boot_fifo_read(data_ptr, data_len);
|
||
|
} else {
|
||
|
mmc_ret = mmc_boot_fifo_write(data_ptr, data_len);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read data to SDC FIFO.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_fifo_read(unsigned int *mmc_ptr, unsigned int data_len)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int mmc_status = 0;
|
||
|
unsigned int mmc_count = 0;
|
||
|
unsigned int read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL |
|
||
|
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | MMC_BOOT_MCI_STAT_RX_OVRRUN;
|
||
|
|
||
|
/* 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 (unsigned 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);
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Write data to SDC FIFO.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_fifo_write(unsigned int *mmc_ptr, unsigned int data_len)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int mmc_status = 0;
|
||
|
unsigned int mmc_count = 0;
|
||
|
unsigned int write_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL |
|
||
|
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | MMC_BOOT_MCI_STAT_TX_UNDRUN;
|
||
|
unsigned int count = 0;
|
||
|
unsigned int sz = 0;
|
||
|
|
||
|
/* Write the transfer data to SDCC3 FIFO */
|
||
|
do {
|
||
|
mmc_status = readl(MMC_BOOT_MCI_STATUS);
|
||
|
|
||
|
/* Bytes left to write */
|
||
|
count = data_len - mmc_count;
|
||
|
|
||
|
/* Break if whole data is transferred */
|
||
|
if (!count)
|
||
|
break;
|
||
|
|
||
|
/* Write half FIFO or less (remaining) words in MCI_FIFO as long as either
|
||
|
TX_FIFO_EMPTY or TX_FIFO_HFULL bits of MCI_STATUS register are set. */
|
||
|
if ((mmc_status & MMC_BOOT_MCI_STAT_TX_FIFO_EMPTY) ||
|
||
|
(mmc_status & MMC_BOOT_MCI_STAT_TX_FIFO_HFULL)) {
|
||
|
|
||
|
/* Write minimum of half FIFO and remaining words */
|
||
|
sz = ((count >> 2) > MMC_BOOT_MCI_HFIFO_COUNT) \
|
||
|
? MMC_BOOT_MCI_HFIFO_COUNT : (count >> 2);
|
||
|
|
||
|
for (int i = 0; i < sz; i++) {
|
||
|
writel(*mmc_ptr, MMC_BOOT_MCI_FIFO);
|
||
|
mmc_ptr++;
|
||
|
/* increase mmc_count by word size */
|
||
|
mmc_count += sizeof(unsigned int);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while (1);
|
||
|
|
||
|
do
|
||
|
{
|
||
|
mmc_status = readl(MMC_BOOT_MCI_STATUS);
|
||
|
if (mmc_status & write_error) {
|
||
|
mmc_ret = mmc_boot_status_error(mmc_status);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
while (!(mmc_status & MMC_BOOT_MCI_STAT_DATA_END));
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CMD35_ERASE_GROUP_START
|
||
|
*/
|
||
|
|
||
|
static unsigned int
|
||
|
mmc_boot_send_erase_group_start(struct mmc_card *card,
|
||
|
unsigned long long data_addr)
|
||
|
{
|
||
|
struct mmc_boot_command cmd;
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
if (card == NULL)
|
||
|
return MMC_BOOT_E_INVAL;
|
||
|
|
||
|
memset((struct mmc_boot_command *)&cmd, 0,
|
||
|
sizeof(struct mmc_boot_command));
|
||
|
|
||
|
cmd.cmd_index = CMD35_ERASE_GROUP_START;
|
||
|
cmd.argument = data_addr;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* Checking for address error */
|
||
|
if (IS_ADDR_OUT_OF_RANGE(cmd.resp[0])) {
|
||
|
return MMC_BOOT_E_BLOCKLEN_ERR;
|
||
|
}
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CMD36 ERASE GROUP END
|
||
|
*/
|
||
|
static unsigned int
|
||
|
mmc_boot_send_erase_group_end(struct mmc_card *card,
|
||
|
unsigned long long data_addr)
|
||
|
{
|
||
|
struct mmc_boot_command cmd;
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
if (card == NULL)
|
||
|
return MMC_BOOT_E_INVAL;
|
||
|
|
||
|
memset((struct mmc_boot_command *)&cmd, 0,
|
||
|
sizeof(struct mmc_boot_command));
|
||
|
|
||
|
cmd.cmd_index = CMD36_ERASE_GROUP_END;
|
||
|
cmd.argument = data_addr;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* Checking for address error */
|
||
|
if (IS_ADDR_OUT_OF_RANGE(cmd.resp[0])) {
|
||
|
return MMC_BOOT_E_BLOCKLEN_ERR;
|
||
|
}
|
||
|
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CMD38 ERASE
|
||
|
*/
|
||
|
static unsigned int mmc_boot_send_erase(struct mmc_card *card)
|
||
|
{
|
||
|
|
||
|
struct mmc_boot_command cmd;
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned int status;
|
||
|
|
||
|
if (card == NULL)
|
||
|
return MMC_BOOT_E_INVAL;
|
||
|
|
||
|
memset((struct mmc_boot_command *)&cmd, 0,
|
||
|
sizeof(struct mmc_boot_command));
|
||
|
|
||
|
cmd.cmd_index = CMD38_ERASE;
|
||
|
cmd.argument = 0x00000000;
|
||
|
cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
|
||
|
cmd.resp_type = MMC_BOOT_RESP_R1B;
|
||
|
|
||
|
/* Checking if the card is in the transfer state */
|
||
|
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_command(&cmd);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/* Checking for write protect */
|
||
|
if (cmd.resp[0] & MMC_BOOT_R1_WP_ERASE_SKIP) {
|
||
|
dprintf(CRITICAL, "Write protect enabled for sector \n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Checking if the erase operation for the card is compelete */
|
||
|
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 erase data on the eMMC card
|
||
|
*/
|
||
|
unsigned int
|
||
|
mmc_erase_card(unsigned long long data_addr, unsigned long long size)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
unsigned long long erase_grp_size;
|
||
|
unsigned long long data_end = 0x00000000;
|
||
|
unsigned long long loop_count;
|
||
|
unsigned int out[512] = { 0 };
|
||
|
|
||
|
/* Converting size to sectors */
|
||
|
size = size / 512;
|
||
|
|
||
|
if (ext_csd_buf[MMC_BOOT_EXT_ERASE_GROUP_DEF]) {
|
||
|
erase_grp_size =
|
||
|
(512 * ext_csd_buf[MMC_BOOT_EXT_HC_ERASE_GRP_SIZE] * 1024);
|
||
|
erase_grp_size = erase_grp_size / 512;
|
||
|
} else {
|
||
|
erase_grp_size = (mmc_card.csd.erase_grp_size + 1) *
|
||
|
(mmc_card.csd.erase_grp_mult + 1);
|
||
|
}
|
||
|
|
||
|
if (erase_grp_size == 0) {
|
||
|
return MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (size % erase_grp_size) {
|
||
|
dprintf(CRITICAL, "Overflow beyond ERASE_GROUP_SIZE:%llu\n",
|
||
|
(size % erase_grp_size));
|
||
|
|
||
|
}
|
||
|
loop_count = (size / erase_grp_size);
|
||
|
/*
|
||
|
*In case the partition size is less than the erase_grp_size
|
||
|
0 is written to the first block of the partition.
|
||
|
*/
|
||
|
if (loop_count < 1) {
|
||
|
mmc_ret = mmc_write(data_addr, 512, (unsigned int *)out);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS)
|
||
|
return mmc_ret;
|
||
|
else
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
} else {
|
||
|
data_addr = ((mmc_card.type != MMC_BOOT_TYPE_MMCHC) &&
|
||
|
(mmc_card.type != MMC_BOOT_TYPE_SDHC))
|
||
|
? (unsigned int)data_addr : (unsigned int)(data_addr / 512);
|
||
|
data_end = data_addr + erase_grp_size * (loop_count - 1);
|
||
|
}
|
||
|
|
||
|
/* Sending CMD35 */
|
||
|
mmc_ret = mmc_boot_send_erase_group_start(&mmc_card, data_addr);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL, "Error %d: Failure sending erase group start "
|
||
|
"command to the card (RCA:%x)\n", mmc_ret,
|
||
|
mmc_card.rca);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/* Sending CMD36 */
|
||
|
mmc_ret = mmc_boot_send_erase_group_end(&mmc_card, data_end);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL, "Error %d: Failure sending erase group end "
|
||
|
"command to the card (RCA:%x)\n", mmc_ret,
|
||
|
mmc_card.rca);
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
/* Sending CMD38 */
|
||
|
mmc_ret = mmc_boot_send_erase(&mmc_card);
|
||
|
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Error %d: Failure sending erase command "
|
||
|
"to the card (RCA:%x)\n", mmc_ret, mmc_card.rca);
|
||
|
return mmc_ret;
|
||
|
|
||
|
}
|
||
|
|
||
|
dprintf(CRITICAL, "ERASE SUCCESSFULLY COMPLETED\n");
|
||
|
return MMC_BOOT_E_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Disable MCI clk
|
||
|
*/
|
||
|
void mmc_boot_mci_clk_disable()
|
||
|
{
|
||
|
uint32_t reg = 0;
|
||
|
|
||
|
reg |= MMC_BOOT_MCI_CLK_DISABLE;
|
||
|
writel(reg, MMC_BOOT_MCI_CLK);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Enable MCI CLK
|
||
|
*/
|
||
|
void mmc_boot_mci_clk_enable()
|
||
|
{
|
||
|
uint32_t reg = 0;
|
||
|
|
||
|
reg |= MMC_BOOT_MCI_CLK_ENABLE;
|
||
|
reg |= MMC_BOOT_MCI_CLK_ENA_FLOW;
|
||
|
reg |= MMC_BOOT_MCI_CLK_IN_FEEDBACK;
|
||
|
writel(reg, MMC_BOOT_MCI_CLK);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
/* Enable power save */
|
||
|
reg |= MMC_BOOT_MCI_CLK_PWRSAVE;
|
||
|
writel(reg, MMC_BOOT_MCI_CLK);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
}
|
||
|
|
||
|
#if MMC_BOOT_BAM
|
||
|
|
||
|
void mmc_boot_dml_init()
|
||
|
{
|
||
|
uint32_t val = 0;
|
||
|
|
||
|
/* Initialize s/w reset for DML core */
|
||
|
mmc_boot_dml_reset();
|
||
|
|
||
|
/* Program DML config:
|
||
|
* 1. Disable producer and consumer CRCI.
|
||
|
* 2. Set Bypass mode for the DML for Direct access.
|
||
|
*/
|
||
|
val = 0;
|
||
|
val |= 1 >> SDCC_BYPASS_SHIFT;
|
||
|
writel(val, SDCC_DML_CONFIG(dml_base));
|
||
|
|
||
|
/* Program consumer logic size:
|
||
|
* This is for handshaking between the BAM and the DML blocks.
|
||
|
*/
|
||
|
writel(4096, SDCC_DML_CONSUMER_PIPE_LOGICAL_SIZE(dml_base));
|
||
|
|
||
|
/* Program producer logic size
|
||
|
* This is for handshaking between the BAM and the DML blocks.
|
||
|
*/
|
||
|
writel(4096, SDCC_DML_PRODUCER_PIPE_LOGICAL_SIZE(dml_base));
|
||
|
|
||
|
|
||
|
/* Write the pipe id numbers. */
|
||
|
val = 0;
|
||
|
val |= bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].pipe_num << SDCC_PRODUCER_PIPE_ID_SHIFT;
|
||
|
val |= bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].pipe_num << SDCC_CONSUMER_PIPE_ID_SHIFT;
|
||
|
|
||
|
writel(val, SDCC_DML_PIPE_ID(dml_base));
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Function to set up SDCC dml for System producer transaction. */
|
||
|
static void mmc_boot_dml_consumer_trans_init()
|
||
|
{
|
||
|
uint32_t val = 0;
|
||
|
|
||
|
val = 0 << SDCC_PRODUCER_CRCI_SEL_SHIFT;
|
||
|
val |= 1 << SDCC_CONSUMER_CRCI_SEL_SHIFT;
|
||
|
writel(val, SDCC_DML_CONFIG(dml_base));
|
||
|
|
||
|
|
||
|
/* Start the consumer transaction */
|
||
|
writel(1, SDCC_DML_CONSUMER_START(dml_base));
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Function to set up SDCC dml for System consumer transaction.
|
||
|
* trans_end: 1: Assert DML trasaction signal
|
||
|
* at the end of transaction.
|
||
|
* 0: Do not assert DML transaction signal.
|
||
|
* size: Transaction size
|
||
|
*/
|
||
|
static void mmc_boot_dml_producer_trans_init(unsigned trans_end,
|
||
|
unsigned size)
|
||
|
{
|
||
|
uint32_t val = 0;
|
||
|
|
||
|
val = 1 << SDCC_PRODUCER_CRCI_SEL_SHIFT;
|
||
|
val |= 0 << SDCC_CONSUMER_CRCI_SEL_SHIFT;
|
||
|
val |= trans_end << SDCC_PRODUCER_TRANS_END_EN_SHIFT;
|
||
|
writel(val, SDCC_DML_CONFIG(dml_base));
|
||
|
|
||
|
/* Set block size */
|
||
|
writel(BLOCK_SIZE, SDCC_DML_PRODUCER_BAM_BLOCK_SIZE(dml_base));
|
||
|
|
||
|
/* Write transaction size */
|
||
|
writel(size, SDCC_DML_PRODUCER_BAM_TRANS_SIZE(dml_base));
|
||
|
|
||
|
/* Start the producer transaction */
|
||
|
writel(1, SDCC_DML_PRODUCER_START(dml_base));
|
||
|
}
|
||
|
|
||
|
/* Function to check producer idle status of the DML.
|
||
|
* return value: 1: Producer is idle
|
||
|
* 0: Producer is busy
|
||
|
*/
|
||
|
static uint32_t mmc_boot_dml_chk_producer_idle()
|
||
|
{
|
||
|
uint32_t val = 0;
|
||
|
|
||
|
val = readl(SDCC_DML_STATUS(dml_base));
|
||
|
|
||
|
/* Read only the producer idle status */
|
||
|
val &= (1 << SDCC_DML_PRODUCER_IDLE_SHIFT);
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
/* Function to clear transaction complete flag */
|
||
|
static void mmc_boot_dml_clr_trans_complete()
|
||
|
{
|
||
|
uint32_t val;
|
||
|
|
||
|
val = readl(SDCC_DML_CONFIG(dml_base));
|
||
|
|
||
|
val &= ~(1 << SDCC_PRODUCER_TRANS_END_EN_SHIFT);
|
||
|
writel(val, SDCC_DML_CONFIG(dml_base));
|
||
|
}
|
||
|
|
||
|
/* Blocking function to wait until DML is idle. */
|
||
|
static void mmc_boot_dml_wait_producer_idle()
|
||
|
{
|
||
|
while(!(readl(SDCC_DML_STATUS(dml_base)) & 1));
|
||
|
}
|
||
|
|
||
|
/* Blocking function to wait until DML is idle. */
|
||
|
static void mmc_boot_dml_wait_consumer_idle()
|
||
|
{
|
||
|
while(!(readl(SDCC_DML_STATUS(dml_base)) & (1 << SDCC_DML_CONSUMER_IDLE_SHIFT)));
|
||
|
}
|
||
|
|
||
|
/* Initialize S/W reset */
|
||
|
static void mmc_boot_dml_reset()
|
||
|
{
|
||
|
/* Initialize s/w reset for DML core */
|
||
|
writel(1, SDCC_DML_SW_RESET(dml_base));
|
||
|
|
||
|
}
|
||
|
|
||
|
static int mmc_bam_init(uint32_t bam_base)
|
||
|
{
|
||
|
|
||
|
uint32_t mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
bam.base = bam_base;
|
||
|
/* Read pipe parameter initializations. */
|
||
|
bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].pipe_num = MMC_BOOT_BAM_READ_PIPE;
|
||
|
/* System consumer */
|
||
|
bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].trans_type = BAM2SYS;
|
||
|
/* Set the descriptor FIFO start ptr */
|
||
|
bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].fifo.head = desc_fifo;
|
||
|
/* Set the descriptor FIFO lengths */
|
||
|
bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].fifo.size = MMC_BOOT_BAM_FIFO_SIZE;
|
||
|
|
||
|
/* Write pipe parameter initializations.*/
|
||
|
bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].pipe_num = MMC_BOOT_BAM_WRITE_PIPE;
|
||
|
/* System producer */
|
||
|
bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].trans_type = SYS2BAM;
|
||
|
/* Write fifo uses the same fifo as read */
|
||
|
bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].fifo.head = desc_fifo;
|
||
|
/* Set the descriptor FIFO lengths */
|
||
|
bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].fifo.size = MMC_BOOT_BAM_FIFO_SIZE;
|
||
|
|
||
|
/* Programs the minimum threshold for BAM transfer*/
|
||
|
bam.threshold = BLOCK_SIZE;
|
||
|
|
||
|
/* Initialize MMC BAM */
|
||
|
bam_init(&bam);
|
||
|
|
||
|
/* Initialize BAM MMC read pipe */
|
||
|
bam_sys_pipe_init(&bam, MMC_BOOT_BAM_READ_PIPE_INDEX);
|
||
|
|
||
|
mmc_ret = bam_pipe_fifo_init(&bam, bam.pipe[MMC_BOOT_BAM_READ_PIPE_INDEX].pipe_num);
|
||
|
|
||
|
if (mmc_ret)
|
||
|
{
|
||
|
dprintf(CRITICAL, "MMC: BAM Read pipe fifo init error\n");
|
||
|
goto mmc_bam_init_error;
|
||
|
}
|
||
|
|
||
|
/* Initialize BAM MMC write pipe */
|
||
|
bam_sys_pipe_init(&bam, MMC_BOOT_BAM_WRITE_PIPE_INDEX);
|
||
|
|
||
|
mmc_ret = bam_pipe_fifo_init(&bam, bam.pipe[MMC_BOOT_BAM_WRITE_PIPE_INDEX].pipe_num);
|
||
|
|
||
|
if (mmc_ret)
|
||
|
{
|
||
|
dprintf(CRITICAL, "MMC: BAM Write pipe fifo init error\n");
|
||
|
goto mmc_bam_init_error;
|
||
|
}
|
||
|
|
||
|
mmc_boot_dml_init();
|
||
|
|
||
|
mmc_bam_init_error:
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
static int mmc_bam_transfer_data(unsigned int *data_ptr,
|
||
|
unsigned int data_len,
|
||
|
unsigned int dir)
|
||
|
{
|
||
|
uint32_t mmc_ret;
|
||
|
uint32_t offset;
|
||
|
|
||
|
mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
if(dir == MMC_BOOT_DATA_READ)
|
||
|
{
|
||
|
/* Check BAM IRQ status reg to verify the desc has been processed */
|
||
|
mmc_ret = bam_wait_for_interrupt(&bam,
|
||
|
MMC_BOOT_BAM_READ_PIPE_INDEX, P_PRCSD_DESC_EN_MASK);
|
||
|
|
||
|
if (mmc_ret != BAM_RESULT_SUCCESS)
|
||
|
{
|
||
|
dprintf(CRITICAL, "BAM transfer error \n");
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
goto mmc_bam_transfer_err;
|
||
|
}
|
||
|
|
||
|
mmc_boot_dml_wait_producer_idle();
|
||
|
|
||
|
/* Update BAM pipe fifo offsets */
|
||
|
offset = bam_read_offset_update(&bam, MMC_BOOT_BAM_READ_PIPE_INDEX);
|
||
|
|
||
|
/* Reset DPSM */
|
||
|
writel(0, MMC_BOOT_MCI_DATA_CTL);
|
||
|
|
||
|
/* Wait for the MMC_BOOT_MCI_DATA_CTL write to go through. */
|
||
|
mmc_mclk_reg_wr_delay();
|
||
|
|
||
|
dprintf(SPEW, "Offset value is %d \n", offset);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Check BAM IRQ status reg to verify the desc has been processed */
|
||
|
mmc_ret = bam_wait_for_interrupt(&bam,
|
||
|
MMC_BOOT_BAM_WRITE_PIPE_INDEX, P_TRNSFR_END_EN_MASK);
|
||
|
|
||
|
if (mmc_ret != BAM_RESULT_SUCCESS)
|
||
|
{
|
||
|
dprintf(CRITICAL, "BAM transfer error \n");
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
goto mmc_bam_transfer_err;
|
||
|
}
|
||
|
|
||
|
/* Update BAM pipe fifo offsets */
|
||
|
offset = bam_read_offset_update(&bam, MMC_BOOT_BAM_WRITE_PIPE_INDEX);
|
||
|
|
||
|
dprintf(SPEW, "Offset value is %d \n", offset);
|
||
|
}
|
||
|
|
||
|
mmc_bam_transfer_err:
|
||
|
|
||
|
return mmc_ret;
|
||
|
}
|
||
|
|
||
|
static unsigned int
|
||
|
mmc_boot_bam_setup_desc(unsigned int *data_ptr,
|
||
|
unsigned int data_len,
|
||
|
unsigned char direction)
|
||
|
{
|
||
|
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
|
||
|
|
||
|
if (direction == MMC_BOOT_DATA_READ)
|
||
|
{
|
||
|
mmc_boot_dml_producer_trans_init(1, data_len);
|
||
|
mmc_ret = bam_add_desc(&bam, MMC_BOOT_BAM_READ_PIPE_INDEX,
|
||
|
(unsigned char *)data_ptr, data_len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mmc_boot_dml_consumer_trans_init();
|
||
|
mmc_ret = bam_add_desc(&bam, MMC_BOOT_BAM_WRITE_PIPE_INDEX,
|
||
|
(unsigned char *)data_ptr, data_len);
|
||
|
}
|
||
|
|
||
|
/* Update return value enums */
|
||
|
if (mmc_ret != BAM_RESULT_SUCCESS)
|
||
|
{
|
||
|
dprintf(CRITICAL, "MMC BAM transfer error: %d\n", mmc_ret);
|
||
|
mmc_ret = MMC_BOOT_E_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Check if card supports DDR mode
|
||
|
*/
|
||
|
uint8_t card_supports_ddr_mode()
|
||
|
{
|
||
|
if (IS_BIT_SET_EXT_CSD(MMC_DEVICE_TYPE, 2) ||
|
||
|
IS_BIT_SET_EXT_CSD(MMC_DEVICE_TYPE, 3))
|
||
|
return 1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check if card suppports HS200 mode
|
||
|
*/
|
||
|
uint8_t card_supports_hs200_mode()
|
||
|
{
|
||
|
if (IS_BIT_SET_EXT_CSD(MMC_DEVICE_TYPE, 4) ||
|
||
|
IS_BIT_SET_EXT_CSD(MMC_DEVICE_TYPE, 5))
|
||
|
return 1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Return the density of the mmc device */
|
||
|
uint64_t mmc_get_device_capacity()
|
||
|
{
|
||
|
return mmc_card.capacity;
|
||
|
}
|