Files
plant_car/drivers/source/mmc/sdrv_mmc_sdhci.c
2025-11-08 13:26:19 +08:00

4156 lines
128 KiB
C

/**
* @file sdrv_mmc_sdhci.c
* @brief sdrv mmc sdhci drv source.
*
* @copyright Copyright (c) 2020 Semidrive Semiconductor.
* All rights reserved.
*/
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <common.h>
#include <compiler.h>
#include <param.h>
#include <sdrv_mmc_sdhci.h>
#include <sdrv_ckgen.h>
#include <sdrv_sdhci.h>
#include <part.h>
#include <sdrv_scr.h>
#define SEHC_MAX_TUNING_LOOP 140
/* SDHCI SEHC REG */
#define SDHCI_DLL_CTRL (0x100)
#define SDHCI_DLL_STATUS (0x104)
#define SDHCI_EMMC_CTRL_REG (0x10C)
#define SDHCI_TUNING_CTRL_REG (0x118)
#define SDHCI_TUNING_STATUS_REG (0x11C)
#define SDHCI_OE_MASK_SEL_REG (0x124)
/* dll related macros */
#define SDHCI_DLL_SLV_TARGET_MASK 0x3f
#define SDHCI_DLL_SLV_TARGET_OFFSET 0x1
#define SDHCI_DLL_EN 0x00000001
#define SDHCI_DLL_REF_LOCK 0x00000001
/* emmc related macros */
#define SDHCI_RST_N 0x100
#define SDHCI_FIX_BLK_SIZE 0x20
#define SDHCI_CLK_STP_BLK_GAP 0x10
#define SDHCI_EMMC_EXT 0x2
#define SDHCI_ENHANCE_HS400_EN 0x1
/* tuning related macros */
#define SDHCI_TUNING_CLK_STOP_EN 0x80000000
#define SDHCI_DELAY_TAP_VALUE_MASK 0x3f
#define SDHCI_DELAY_TAP_VALUE_OFFSET 0x10
#define SDHCI_OVERRIDE_VAL_MASK 0x7f
#define SDHCI_OVERRIDE_VAL_OFFSET 0x9
#define SDHCI_OVERRIDE_EN 0x00000100
#define SDHCI_TAP_STEP_MASK 0x3
#define SDHCI_TAP_STEP_OFFSET 0x6
#define SDHCI_START_TAP_MASK 0x3f
#define SDHCI_START_TAP_OFFSET 0x0
#define SDHCI_TUNING_TAP_VALUE_MASK 0x7f
#define SDHCI_TUNING_TAP_VALUE_OFFSET 0x14
#define SDHCI_FAIL_NUM_MASK 0x7f
#define SDHCI_FAIL_NUM_OFFSET 0xc
#define SDHCI_PASS_NUM_MASK 0x7f
#define SDHCI_PASS_NUM_OFFSET 0x4
#define SDHCI_SEC_FAIL_FLAG 0x00000008
#define SDHCI_FIR_PASS_FLAG 0x00000004
#define SDHCI_FIR_FAIL_FLAG 0x00000002
#define SDHCI_TUNING_FAIL 0x00000001
/* SEHC TUNING related macros */
#define SEHC_TUNE_CTRL_MIN 0
#define SEHC_1_0_TUNE_CTRL_MIN 20
#define SEHC_TUNE_CTRL_MAX 128
#define SEHC_TUNE_CTRL_STEP 1
#define SEHC_TUNE_WINDOW_MIN 5
/* SDHCI OE related macros */
#define SDHCI_CMD_OE_MASK_SEL 0x3
#define SDHCI_CMD_OE_OFFSET 0x0
#define SDHCI_DAT_OE_MASK_SEL 0xc
#define SDHCI_DAT_OE_OFFSET 0x2
// NOTE: for test on zone, need define WITH_ON_ZONE macro
//#define WITH_ON_ZONE 1
#define USE_TARGET_HS200_CAPS 1
/* data access time unit in ns */
static const uint32_t taac_unit[] = {1, 10, 100, 1000,
10000, 100000, 1000000, 10000000
};
/* data access time value x 10 */
static const uint32_t 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 uint32_t xfer_rate_unit[] = {100, 1000, 10000, 100000, 0, 0, 0, 0};
/* data transfer rate value x 10*/
static const uint32_t xfer_rate_value[] = {0, 10, 12, 13, 15, 20, 26, 30,
35, 40, 45, 52, 55, 60, 70, 80
};
#define TUNING_BLK_SIZE 128
static uint8_t tuning_data[TUNING_BLK_SIZE] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
static const uint8_t tuning_blk_pattern_4bit[] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
static const uint8_t tuning_blk_pattern_8bit[] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
/*
* Function: mmc decode and save csd
* Arg : Card structure & raw csd
* Return : 0 on Success, 1 on Failure
* Flow : 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 status_t sdrv_mmc_decode_and_save_csd(struct mmc_card *card)
{
uint32_t mmc_sizeof = 0;
uint32_t mmc_unit = 0;
uint32_t mmc_value = 0;
uint32_t mmc_temp = 0;
uint32_t *raw_csd = card->raw_csd;
struct mmc_csd mmc_csd;
mmc_sizeof = sizeof(uint32_t) * 8;
mmc_csd.cmmc_structure = UNPACK_BITS(raw_csd, 126, 2, mmc_sizeof);
if (MMC_CARD_SD(card)) {
/* 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);
/* Fixed value is 9 = 2^9 = 512 */
mmc_csd.write_blk_len = 512;
/* Fixed value is 9 = 512 */
mmc_csd.read_blk_len = 512;
/* Fixed value: 010b */
mmc_csd.r2w_factor = 0x2;
/* Not there in version 2.0 */
mmc_csd.c_size_mult = 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;
mmc_unit = UNPACK_BITS(raw_csd, 112, 3, mmc_sizeof);
mmc_value = UNPACK_BITS(raw_csd, 115, 4, mmc_sizeof);
mmc_csd.taac_ns = 0; /* Unused */
mmc_csd.taac_clks = 0; /* Unused */
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 =
(unsigned long long)(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] + 9) / 10;
mmc_csd.taac_clks = UNPACK_BITS(raw_csd, 104, 8, mmc_sizeof) * 100;
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 =
(unsigned long long)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] + 9) / 10;
mmc_csd.taac_clks = UNPACK_BITS(raw_csd, 104, 8, mmc_sizeof) * 100;
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);
/* Ignore -- no use of this value. */
mmc_csd.tran_speed = 0x00;
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 =
(unsigned long long)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.
*/
uint64_t sec_count;
sec_count =
(card->ext_csd[MMC_SEC_COUNT4] << MMC_SEC_COUNT4_SHIFT) |
(card->ext_csd[MMC_SEC_COUNT3] << MMC_SEC_COUNT3_SHIFT) |
(card->ext_csd[MMC_SEC_COUNT2] << MMC_SEC_COUNT2_SHIFT) |
card->ext_csd[MMC_SEC_COUNT1];
card->capacity = sec_count * MMC_BLK_SZ;
}
}
/* save the information in card structure */
memcpy(&card->csd, &mmc_csd, sizeof(struct mmc_csd));
/* Calculate the wp grp size */
if (MMC_CARD_MMC(card)) {
if (card->ext_csd[MMC_ERASE_GRP_DEF])
card->wp_grp_size = MMC_HC_ERASE_MULT *
card->ext_csd[MMC_HC_ERASE_GRP_SIZE] /
MMC_BLK_SZ;
else
card->wp_grp_size = (card->csd.wp_grp_size + 1) *
(card->csd.erase_grp_size + 1) *
(card->csd.erase_grp_mult + 1);
card->boot_size = BOOT_PART_MIN_SIZE * card->ext_csd[BOOT_SIZE_MULT];
card->rpmb_size = RPMB_PART_MIN_SIZE * card->ext_csd[RPMB_SIZE_MULT];
card->rel_wr_count = card->ext_csd[REL_WR_SEC_C];
}
else {
card->wp_grp_size = (card->csd.wp_grp_size + 1) *
(card->csd.erase_grp_size + 1) *
(card->csd.erase_grp_mult + 1);
}
ssdk_printf(SSDK_INFO, "Decoded CSD fields:\r\n");
ssdk_printf(SSDK_INFO, "cmmc_structure: %u\r\n", mmc_csd.cmmc_structure);
ssdk_printf(SSDK_INFO, "card_cmd_class: %x\r\n", mmc_csd.card_cmd_class);
ssdk_printf(SSDK_INFO, "write_blk_len: %u\r\n", mmc_csd.write_blk_len);
ssdk_printf(SSDK_INFO, "read_blk_len: %u\r\n", mmc_csd.read_blk_len);
ssdk_printf(SSDK_INFO, "r2w_factor: %u\r\n", mmc_csd.r2w_factor);
ssdk_printf(SSDK_INFO, "sector_size: %u\r\n", mmc_csd.sector_size);
ssdk_printf(SSDK_INFO, "c_size_mult:%u\r\n", mmc_csd.c_size_mult);
ssdk_printf(SSDK_INFO, "c_size: %u\r\n", mmc_csd.c_size);
ssdk_printf(SSDK_INFO, "nsac_clk_cycle: %u\r\n", mmc_csd.nsac_clk_cycle);
ssdk_printf(SSDK_INFO, "taac_ns: %u\r\n", mmc_csd.taac_ns);
ssdk_printf(SSDK_INFO, "taac_clks: %u\r\n", mmc_csd.taac_clks);
ssdk_printf(SSDK_INFO, "tran_speed: %u kbps\r\n", mmc_csd.tran_speed);
ssdk_printf(SSDK_INFO, "erase_blk_len: %u\r\n", mmc_csd.erase_blk_len);
ssdk_printf(SSDK_INFO, "read_blk_misalign: %u\r\n", mmc_csd.read_blk_misalign);
ssdk_printf(SSDK_INFO, "write_blk_misalign: %u\r\n",
mmc_csd.write_blk_misalign);
ssdk_printf(SSDK_INFO, "read_blk_partial: %u\r\n", mmc_csd.read_blk_partial);
ssdk_printf(SSDK_INFO, "write_blk_partial: %u\r\n", mmc_csd.write_blk_partial);
ssdk_printf(SSDK_INFO, "wp_grp_size: %u\r\n", card->wp_grp_size);
ssdk_printf(SSDK_INFO, "Card Capacity: %llu Bytes\r\n", card->capacity);
return SDRV_STATUS_OK;
}
/*
* Function: mmc decode & save cid
* Arg : card structure & raw cid
* Return : 0 on Success, 1 on Failure
* Flow : Decode CID sent by the card.
*/
static status_t sdrv_mmc_decode_and_save_cid(struct mmc_card *card,
uint32_t *raw_cid)
{
struct mmc_cid mmc_cid;
uint32_t mmc_sizeof = 0;
int i = 0;
if (!raw_cid) {
return SDRV_STATUS_INVALID_PARAM;
}
mmc_sizeof = sizeof(uint32_t) * 8;
if (MMC_CARD_SD(card)) {
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] = (uint8_t)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] = (uint8_t)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(&card->cid, &mmc_cid, sizeof(struct mmc_cid));
ssdk_printf(SSDK_INFO, "Decoded CID fields:\r\n");
ssdk_printf(SSDK_INFO, "Manufacturer ID: %x\r\n", mmc_cid.mid);
ssdk_printf(SSDK_INFO, "OEM ID: 0x%x\r\n", mmc_cid.oid);
ssdk_printf(SSDK_INFO, "Product Name: %s\r\n", mmc_cid.pnm);
ssdk_printf(SSDK_INFO, "Product revision: %d.%d\r\n", (mmc_cid.prv >> 4),
(mmc_cid.prv & 0xF));
ssdk_printf(SSDK_INFO, "Product serial number: %X\r\n", mmc_cid.psn);
ssdk_printf(SSDK_INFO, "Manufacturing date: %d %d\r\n", mmc_cid.month,
mmc_cid.year);
return SDRV_STATUS_OK;
}
/*
* Function: mmc reset cards
* Arg : host structure
* Return : 0 on Success, 1 on Failure
* Flow : Reset all the cards to idle condition (CMD 0)
*/
static status_t sdrv_mmc_reset_card(struct sdhci_host *host)
{
struct mmc_command cmd;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.cmd_index = CMD0_GO_IDLE_STATE;
#ifdef WITH_ON_ZONE
/* if on zone, need use argument for go to pre-idel state */
cmd.argument = 0xF0F0F0F0;
#else
cmd.argument = 0;
#endif
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_NONE;
/* send command */
return sdrv_sdhci_send_command(host, &cmd);
}
/*
* Function: mmc operations command
* Arg : host & card structure
* Return : 0 on Success, 1 on Failure
* Flow : Send CMD1 to know whether the card supports host VDD profile or
* not.
*/
static status_t sdrv_mmc_send_op_cond(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
uint32_t mmc_resp = 0;
status_t mmc_ret = SDRV_STATUS_OK;
uint32_t mmc_retry = 0;
memset(&cmd, 0, sizeof(struct mmc_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 = card->ocr;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R3;
do {
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
/* Command returned success, now it's time to examine response */
mmc_resp = cmd.resp[0];
/* Check the response for busy status */
if (!(mmc_resp & MMC_OCR_BUSY)) {
mmc_retry++;
udelay(1);
continue;
}
else
break;
}
while (mmc_retry < MMC_MAX_COMMAND_RETRY);
/* If we reached here after max retries, we failed to get OCR */
if (mmc_retry == MMC_MAX_COMMAND_RETRY && !(mmc_resp & MMC_OCR_BUSY)) {
DETAIL_PRINTF(SSDK_CRIT, "Card has busy status set. Init did not complete\r\n");
return SDRV_STATUS_BUSY;
}
/* Response contains card's ocr. Update card's information */
card->ocr = mmc_resp;
if (mmc_resp & MMC_OCR_SEC_MODE)
card->type = MMC_TYPE_MMCHC;
else
card->type = MMC_TYPE_STD_MMC;
return mmc_ret;
}
/*
* Function: mmc send cid
* Arg : host & card structure
* Return : 0 on Success, 1 on Failure
* Flow : Request any card to send its uniquie card identification
* (CID) number (CMD2).
*/
static status_t sdrv_mmc_all_send_cid(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD2 Format:
* [31:0] stuff bits
*/
cmd.cmd_index = CMD2_ALL_SEND_CID;
cmd.argument = 0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R2;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
return mmc_ret;
}
/* Response contains card's 128 bits CID register */
mmc_ret = sdrv_mmc_decode_and_save_cid(card, cmd.resp);
if (mmc_ret) {
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc send relative address
* Arg : host & card structure
* Return : 0 on Success, 1 on Failure
* Flow : Ask 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 status_t sdrv_mmc_send_relative_address(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD3 Format:
* [31:0] stuff bits
*/
if (MMC_CARD_SD(card)) {
cmd.cmd_index = CMD3_SEND_RELATIVE_ADDR;
cmd.argument = 0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R6;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
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 = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R6;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc send csd
* Arg : host, card structure & o/p arg to store csd
* Return : 0 on Success, 1 on Failure
* Flow : Requests card to send it's CSD register's contents. (CMD9)
*/
static status_t sdrv_mmc_send_csd(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
uint32_t mmc_arg = 0;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_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 = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R2;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
/* response contains the card csd */
memcpy(card->raw_csd, cmd.resp, sizeof(cmd.resp));
return mmc_ret;
}
/*
* Function: mmc select card
* Arg : host, card structure
* Return : 0 on Success, 1 on Failure
* Flow : 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 status_t sdrv_mmc_select_card(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
uint32_t mmc_arg = 0;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD7 Format:
* [31:16] RCA
* [15:0] stuff bits
*/
mmc_arg |= card->rca << 16;
cmd.cmd_index = CMD7_SELECT_DESELECT_CARD;
cmd.argument = mmc_arg;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.retries = MMC_CMD_RETRIES;
/* If we are deselecting card, we do not get response */
if (card->rca) {
if (MMC_CARD_SD(card))
cmd.resp_type = SDHCI_CMD_RESP_R1B;
else
cmd.resp_type = SDHCI_CMD_RESP_R1;
}
else
cmd.resp_type = SDHCI_CMD_RESP_NONE;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
return mmc_ret;
}
#if 0
/*
* Function: mmc set block len
* Arg : host, card structure & block length
* Return : 0 on Success, 1 on Failure
* Flow : Send command to set block length.
*/
static uint32_t sdrv_mmc_set_block_len(struct sdhci_host *host,
struct mmc_card *card, uint32_t block_len)
{
struct mmc_command cmd;
uint32_t mmc_ret = 0;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD16 Format:
* [31:0] block length
*/
cmd.cmd_index = CMD16_SET_BLOCKLEN;
cmd.argument = block_len;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
/*
* If blocklength is larger than 512 bytes,
* the card sets BLOCK_LEN_ERROR bit.
*/
if (cmd.resp[0] & MMC_R1_BLOCK_LEN_ERR) {
DETAIL_PRINTF(SSDK_CRIT, "The block length is not supported by the card\r\n");
return 1;
}
return 0;
}
#endif
/*
* Function: mmc get card status
* Arg : host, card structure & o/p argument card status
* Return : 0 on Success, 1 on Failure
* Flow : Get the current status of the card
*/
static status_t sdrv_mmc_get_card_status(struct sdhci_host *host,
struct mmc_card *card, uint32_t *status)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD13 Format:
* [31:16] RCA
* [15:0] stuff bits
*/
cmd.cmd_index = CMD13_SEND_STATUS;
cmd.argument = card->rca << 16;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
/* Checking ADDR_OUT_OF_RANGE error in CMD13 response */
if ((cmd.resp[0] >> 31) & 0x01) {
DETAIL_PRINTF(SSDK_CRIT, "CMD13 response ADDR_OUT_OF_RANGE failed\r\n");
return SDRV_STATUS_INVALID_PARAM;
}
*status = cmd.resp[0];
return mmc_ret;
}
/*
* Function: mmc get ext csd
* Arg : host, card structure & array to hold ext attributes
* Return : 0 on Success, 1 on Failure
* Flow : Send ext csd command & get the card attributes
*/
static status_t sdrv_mmc_get_ext_csd(struct sdhci_host *host,
struct mmc_card *card)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
#if WITH_KERNEL_VM
void *vptr;
uint8_t align_pow2 = log2_uint(CONFIG_ARCH_CACHE_LINE);
if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "mmc_ext",
EXT_CSD_BUF_SIZE, &vptr, align_pow2, 0,
ARCH_MMU_FLAG_CACHED) < 0) {
printf("Failed to allocate ext csd buf\r\n");
return SDRV_STATUS_FAIL;
}
card->ext_csd = vptr;
#endif
ASSERT(card->ext_csd);
memset(card->ext_csd, 0, EXT_CSD_BUF_SIZE);
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD8 */
cmd.cmd_index = CMD8_SEND_EXT_CSD;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.data.data_ptr = card->ext_csd;
cmd.data.num_blocks = 1;
cmd.data_present = 0x1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
return mmc_ret;
}
/*
* Function: mmc switch command
* Arg : Host, card structure, access mode, index & value to be set
* Return : 0 on Success, 1 on Failure
* Flow : Send switch command to the card to set the ext attribute @ index
*/
static status_t sdrv_mmc_switch_cmd(struct sdhci_host *host,
struct mmc_card *card,
uint32_t access, uint32_t index, uint32_t value)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
uint32_t mmc_status;
memset(&cmd, 0, sizeof(struct mmc_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 = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1B;
cmd.retries = MMC_CMD_RETRIES;
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "CMD6 send failed\r\n");
return mmc_ret;
}
if (index == MMC_EXT_MMC_HS_TIMING && value == SDHCI_EMMC_HISPEED_MODE) {
/* Set the high speed mode in controller */
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_EMMC_HISPEED_MODE);
if (host->ops->set_clock)
mmc_ret = host->ops->set_clock(host, MMC_CLK_50MHZ);
}
/* Check if the card completed the switch command processing */
mmc_ret = sdrv_mmc_get_card_status(host, card, &mmc_status);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Get card status failed\r\n");
return mmc_ret;
}
if (MMC_CARD_STATUS(mmc_status) != MMC_TRAN_STATE) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd failed. Card not in tran state %x\r\n",
mmc_status);
mmc_ret = SDRV_STATUS_MMC_CARD_STATUS_ERR;
}
if (mmc_status & MMC_SWITCH_FUNC_ERR_FLAG) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd failed. Switch Error.\r\n");
mmc_ret = SDRV_STATUS_MMC_SWITCH_FUNC_ERR;
}
return mmc_ret;
}
bool sdrv_mmc_set_drv_type(struct sdhci_host *host, struct mmc_card *card,
uint8_t drv_type)
{
uint32_t ret = 0;
bool drv_type_changed = false;
uint32_t value = ((drv_type << 4) | MMC_HS200_TIMING);
if (card->ext_csd[MMC_EXT_MMC_DRV_STRENGTH] & (1 << drv_type))
ret = sdrv_mmc_switch_cmd(host, card, MMC_ACCESS_WRITE,
MMC_EXT_MMC_HS_TIMING, value);
if (!ret)
drv_type_changed = true;
return drv_type_changed;
}
/*
* Function: mmc set bus width
* Arg : Host, card structure & width
* Return : 0 on Success, 1 on Failure
* Flow : Send switch command to set bus width
*/
static status_t sdrv_mmc_set_bus_width(struct sdhci_host *host,
struct mmc_card *card, uint32_t width)
{
status_t mmc_ret = SDRV_STATUS_OK;
mmc_ret = sdrv_mmc_switch_cmd(host, card, MMC_ACCESS_WRITE,
MMC_EXT_MMC_BUS_WIDTH, width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd failed\r\n");
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc card supports hs400 mode
* Arg : None
* Return : 1 if hs400 mode is supported, 0 otherwise
* Flow : Check the ext csd attributes of the card
*/
static uint8_t sdrv_mmc_card_supports_hs400_mode(struct mmc_card *card)
{
if (card->ext_csd[MMC_DEVICE_TYPE] & MMC_HS_HS400_MODE)
return 1;
else
return 0;
}
/*
* Function: mmc card supports hs200 mode
* Arg : None
* Return : 1 if HS200 mode is supported, 0 otherwise
* Flow : Check the ext csd attributes of the card
*/
static uint8_t sdrv_mmc_card_supports_hs200_mode(struct mmc_card *card)
{
if (card->ext_csd[MMC_DEVICE_TYPE] & MMC_HS_HS200_MODE)
return 1;
else
return 0;
}
/*
* Function: mmc card supports ddr mode
* Arg : None
* Return : 1 if DDR mode is supported, 0 otherwise
* Flow : Check the ext csd attributes of the card
*/
static uint8_t sdrv_mmc_card_supports_ddr_mode(struct mmc_card *card)
{
if (card->ext_csd[MMC_DEVICE_TYPE] & MMC_HS_DDR_MODE)
return 1;
else
return 0;
}
/*
* Function : Enable HS200 mode
* Arg : Host, card structure and bus width
* Return : 0 on Success, 1 on Failure
* Flow :
* - Set the bus width to 4/8 bit SDR as supported by the target &
* host
* - Set the HS_TIMING on ext_csd 185 for the card
*/
static status_t sdrv_mmc_set_hs200_mode(struct sdhci_host *host,
struct mmc_card *card, uint32_t width)
{
status_t mmc_ret = SDRV_STATUS_OK;
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling HS200 Mode Start\r\n");
/* Set 4/8 bit SDR bus width */
mmc_ret = sdrv_mmc_set_bus_width(host, card, width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set wide bus for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
/* Setting HS200 in HS_TIMING using EXT_CSD (CMD6) */
mmc_ret = sdrv_mmc_switch_cmd(host, card, MMC_ACCESS_WRITE,
MMC_EXT_MMC_HS_TIMING, MMC_HS200_TIMING);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd returned failure %d\r\n", __LINE__);
return mmc_ret;
}
/* Enable HS200 mode in controller */
MMC_SAVE_TIMING(host, SDHCI_EMMC_HS200_MODE);
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_EMMC_HS200_MODE);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, MMC_CLK_200MHZ);
if (mmc_ret)
return mmc_ret;
}
/* Execute Tuning for hs200 mode */
if ((mmc_ret = sdrv_sdhci_execute_tuning(host, CMD21_SEND_TUNING_BLOCK, width)))
DETAIL_PRINTF(SSDK_CRIT, "Tuning for hs200 failed\r\n");
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling HS200 Mode Done\r\n");
return mmc_ret;
}
/*
* Function: mmc set ddr mode
* Arg : Host, card structure and bus width
* Return : 0 on Success, 1 on Failure
* Flow : Set bus width for ddr mode & set controller in DDR mode
*/
static status_t sdrv_mmc_set_ddr_mode(struct sdhci_host *host,
struct mmc_card *card,
uint32_t width)
{
status_t mmc_ret = SDRV_STATUS_OK;
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling DDR Mode Start\r\n");
/* Set 4/8 bit DDR bus width */
if (width == MMC_DATA_BUS_WIDTH_4BIT)
mmc_ret = sdrv_mmc_set_bus_width(host, card, MMC_DATA_DDR_BUS_WIDTH_4BIT);
else if (width == MMC_DATA_BUS_WIDTH_8BIT)
mmc_ret = sdrv_mmc_set_bus_width(host, card, MMC_DATA_DDR_BUS_WIDTH_8BIT);
else {
DETAIL_PRINTF(SSDK_CRIT, "DDR mode not suport bus width: %u\r\n", width);
return SDRV_STATUS_INVALID_PARAM;
}
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set DDR mode for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
/* Save the timing value, before changing the clock */
MMC_SAVE_TIMING(host, SDHCI_EMMC_DDR52_MODE);
/* Set the DDR mode in controller */
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_EMMC_DDR52_MODE);
if (host->ops->set_clock)
mmc_ret = host->ops->set_clock(host, MMC_CLK_50MHZ);
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling DDR Mode Done\r\n");
return mmc_ret;
}
/*
* Function: mmc set high speed interface
* Arg : Host & card structure
* Return : None
* Flow : Sets the host uhs mode & clock
* Adjust the interface speed to optimal speed
*/
static status_t sdrv_mmc_set_hs_interface(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
/* Setting HS_TIMING in EXT_CSD (CMD6) */
mmc_ret = sdrv_mmc_switch_cmd(host, card, MMC_ACCESS_WRITE,
MMC_EXT_MMC_HS_TIMING, MMC_HS_TIMING);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd returned failure %d\r\n", __LINE__);
return mmc_ret;
}
/* Save the timing value, before changing the clock */
MMC_SAVE_TIMING(host, SDHCI_EMMC_HISPEED_MODE);
return mmc_ret;
}
/*
* Function : Enable HS400 mode
* Arg : Host, card structure and bus width
* Return : 0 on Success, 1 on Failure
* Flow :
* - Set the bus width to 8 bit DDR
* - Set the HS_TIMING on ext_csd 185 for the card
*/
status_t sdrv_mmc_set_hs400_mode(struct sdhci_host *host, struct mmc_card *card,
uint32_t width)
{
status_t mmc_ret = SDRV_STATUS_OK;
/*
* Emmc 5.0 spec does not allow changing to hs400 mode directly
* Need to follow the sequence to change to hs400 mode
* 1. Enable HS200 mode, perform tuning
* 2. Change to high speed mode
* 3. Enable DDR mode
* 4. Enable HS400 mode, note that hs400 tuning is done in enable hs200 step
*/
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling HS400 Mode Start\r\n");
/* HS400 mode is supported only in DDR 8-bit */
if (width != MMC_DATA_BUS_WIDTH_8BIT) {
DETAIL_PRINTF(SSDK_CRIT,
"Bus width is not 8-bit, cannot switch to hs400: %u\r\n",
width);
return SDRV_STATUS_INVALID_PARAM;
}
/* 1.Enable HS200 mode */
mmc_ret = sdrv_mmc_set_hs200_mode(host, card, width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure Setting HS200 mode %s\t%d\r\n", __func__,
__LINE__);
return mmc_ret;
}
/* 2. Enable High speed mode */
mmc_ret = sdrv_mmc_set_hs_interface(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error adjusting interface speed!:%s\t%d\r\n",
__func__,
__LINE__);
return mmc_ret;
}
/*3. Enable DDR mode */
mmc_ret = sdrv_mmc_set_ddr_mode(host, card, width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure setting DDR mode:%s\t%d\r\n", __func__,
__LINE__);
return mmc_ret;
}
/*4. Set hs400 timing */
mmc_ret = sdrv_mmc_switch_cmd(host, card, MMC_ACCESS_WRITE,
MMC_EXT_MMC_HS_TIMING, MMC_HS400_TIMING);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Switch cmd returned failure %s\t%d\r\n", __func__,
__LINE__);
return mmc_ret;
}
/* Enable HS400 mode in controller */
/* Save the timing value, before changing the clock */
MMC_SAVE_TIMING(host, SDHCI_EMMC_HS400_MODE);
/* Set the clock back to 200 MHZ */
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, MMC_CLK_200MHZ);
if (mmc_ret)
return mmc_ret;
}
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_EMMC_HS400_MODE);
DETAIL_PRINTF(SSDK_DEBUG, "\r\n Enabling HS400 Mode Done\r\n");
return mmc_ret;
}
status_t sdrv_mmc_send_tuning(struct mmc_device *dev, uint32_t opcode,
uint32_t bus_width)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
const uint8_t *tuning_block_pattern = NULL;
int size;
/* Judgment tuning data */
if (opcode == CMD21_SEND_TUNING_BLOCK &&
bus_width == MMC_DATA_BUS_WIDTH_8BIT) {
tuning_block_pattern = tuning_blk_pattern_8bit;
size = sizeof(tuning_blk_pattern_8bit);
}
else if (bus_width == MMC_DATA_BUS_WIDTH_4BIT) {
tuning_block_pattern = tuning_blk_pattern_4bit;
size = sizeof(tuning_blk_pattern_4bit);
}
else
return SDRV_STATUS_INVALID_PARAM;
/* Configuration commands */
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.cmd_index = opcode;
cmd.argument = 0x0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
/* Configuration data */
cmd.data.data_ptr = tuning_data;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = size;
memset(tuning_data, 0, size);
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret) {
udelay(100);
DETAIL_PRINTF(SSDK_INFO, "sdrv_mmc_send_tuning send error!\r\n");
return mmc_ret;
}
/* Bad judgment */
if (cmd.error) {
udelay(100);
DETAIL_PRINTF(SSDK_INFO, "sdrv_mmc_send_tuning cmd error! reg = 0x%08x\r\n",
cmd.error);
return SDRV_STATUS_MMC_CMD_ERR;
}
if (cmd.data_present && dev->host.data_cmd.error) {
udelay(100);
DETAIL_PRINTF(SSDK_INFO, "sdrv_mmc_send_tuning data error! 0x%08x\r\n",
dev->host.data_cmd.error);
return SDRV_STATUS_MMC_DAT_ERR;
}
/* Data comparison */
if (memcmp(tuning_data, tuning_block_pattern, size)) {
udelay(100);
DETAIL_PRINTF(SSDK_INFO, "memcmp tuning_data error\r\n");
return SDRV_STATUS_MMC_TUNING_FAIL;
}
return mmc_ret;
}
/*
* Function: sdrv_mmc_host_init
* Arg : mmc device structure
* Return : 0 on success, 1 on Failure
* Flow : Initialize the host contoller
* Set the clock rate to 400 KHZ for init
*/
static status_t sdrv_mmc_host_init(struct mmc_device *dev)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct sdhci_host *host;
struct mmc_config_data *cfg;
host = &dev->host;
cfg = &dev->config;
host->base = cfg->sdhc_base;
host->slot = cfg->slot;
host->irq = cfg->irq;
host->caps.hs200_support = cfg->hs200_support;
host->caps.hs400_support = cfg->hs400_support;
host->caps.uhs_support = cfg->uhs_support;
host->caps.hw_reset_support = cfg->hw_reset_support;
host->max_clk_rate = cfg->max_clk_rate;
if (cfg->voltage)
host->caps.voltage = cfg->voltage;
/*
* Initialize the controller, read the host capabilities
* set power on mode
*/
sdrv_sdhci_init(host);
host->caps.ddr_support &= cfg->ddr50_support;
if (host->ops->config_pin)
host->ops->config_pin(host);
return mmc_ret;
}
/*
* Function: mmc identify card
* Arg : host & card structure
* Return : 0 on Success, 1 on Failure
* Flow : Performs card identification process:
* 1. Get card's unique identification number (CID)
* 2. Get(for sd)/set (for mmc) relative card address (RCA)
* 3. Select the card to put it in TRAN state
*/
static status_t sdrv_mmc_identify_card(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
/* Ask card to send its unique card identification (CID) number (CMD2) */
mmc_ret = sdrv_mmc_all_send_cid(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure getting card's CID number!\r\n");
return mmc_ret;
}
/* Ask card to send a relative card address (RCA) (CMD3) */
mmc_ret = sdrv_mmc_send_relative_address(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure getting card's RCA!\r\n");
return mmc_ret;
}
/* Get card's CSD register (CMD9) */
mmc_ret = sdrv_mmc_send_csd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure getting card's CSD information!\r\n");
return mmc_ret;
}
/* Select the card (CMD7) */
mmc_ret = sdrv_mmc_select_card(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure selecting the Card with RCA: %x\r\n",
card->rca);
return mmc_ret;
}
/* Set the card status as active */
card->status = MMC_STATUS_ACTIVE;
return mmc_ret;
}
/*
* Function: sdrv_mmc_reset_card_and_send_op
* Arg : Host & Card structure
* Return : 0 on Success, 1 on Failure
* Flow : Routine to initialize MMC card. It resets a card to idle state,
* verify operating voltage and set the card in ready state.
*/
static status_t sdrv_mmc_reset_card_and_send_op(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
/* 1. Card Reset - CMD0, for card powerup need some delay */
udelay(1000);
mmc_ret = sdrv_mmc_reset_card(host);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure resetting MMC cards!\r\n");
return mmc_ret;
}
/* 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_ret = sdrv_mmc_send_op_cond(host, card);
/* OCR is not received, init could not complete */
if (mmc_ret) {
DETAIL_PRINTF(SSDK_ERR, "Failure getting OCR response from MMC Card\r\n");
return mmc_ret;
}
return mmc_ret;
}
static status_t sdrv_mmc_send_app_cmd(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
cmd.cmd_index = CMD55_APP_CMD;
cmd.argument = (card->rca << 16);
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed Sending CMD55\r\n");
return mmc_ret;
}
return mmc_ret;
}
static status_t sdrv_mmc_switch_uhs_voltage(struct sdhci_host *host)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
cmd.cmd_index = CMD11_SWITCH_VOLTAGE;
cmd.argument = 0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "The CMD11 no response\r\n");
return mmc_ret;
}
udelay(1000);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, 0);
if (mmc_ret)
return mmc_ret;
}
host->caps.voltage = SDHCI_VOL_1_8;
host->ops->set_power(host);
udelay(10000);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_25MHZ);
if (mmc_ret)
return mmc_ret;
}
udelay(1000);
return mmc_ret;
}
status_t sdrv_mmc_sd_card_init(struct sdhci_host *host, struct mmc_card *card)
{
uint8_t i;
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
memset(&cmd, 0, sizeof(struct mmc_command));
/* Use the SD card RCA 0x0 during init */
card->rca = SD_CARD_RCA;
/* Send CMD8 for voltage check*/
for (i = 0; i < SD_CMD8_MAX_RETRY; i++) {
cmd.cmd_index = CMD8_SEND_IF_COND;
cmd.argument = MMC_SD_HC_VOLT_SUPPLIED;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R7;
cmd.retries = MMC_CMD_RETRIES;
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT,
"The response for CMD8 does not match the supplied value\r\n");
return mmc_ret;
}
else {
/* If the command response echos the voltage back */
if (cmd.resp[0] == MMC_SD_HC_VOLT_SUPPLIED)
break;
}
udelay(1);
}
if (i == SD_CMD8_MAX_RETRY && (cmd.resp[0] != MMC_SD_HC_VOLT_SUPPLIED)) {
DETAIL_PRINTF(SSDK_CRIT, "Error: CMD8 response timed out\r\n");
return SDRV_STATUS_TIMEOUT;
}
/* Send ACMD41 for OCR */
for (i = 0; i < SD_ACMD41_MAX_RETRY; i++) {
/* Send APP_CMD before ACMD41*/
mmc_ret = sdrv_mmc_send_app_cmd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed sending App command\r\n");
return mmc_ret;
}
/* APP_CMD is successful, send ACMD41 now */
cmd.cmd_index = ACMD41_SEND_OP_COND;
cmd.argument = MMC_SD_OCR | MMC_SD_HC_HCS | MMC_SD_S18R;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R3;
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure sending ACMD41\r\n");
return mmc_ret;
}
else {
if (cmd.resp[0] & MMC_SD_DEV_READY) {
if (cmd.resp[0] & (1 << 30)) {
if (host->caps.uhs_support && cmd.resp[0] & (1 << 24))
card->type = MMC_CARD_TYPE_UHS;
else
card->type = MMC_CARD_TYPE_SDHC;
}
else {
card->type = MMC_CARD_TYPE_STD_SD;
}
break;
}
}
udelay(50000);
}
if (i == SD_ACMD41_MAX_RETRY && !(cmd.resp[0] & MMC_SD_DEV_READY)) {
DETAIL_PRINTF(SSDK_CRIT, "Error: ACMD41 response timed out\r\n");
return SDRV_STATUS_TIMEOUT;
}
if (card->type == MMC_CARD_TYPE_UHS) {
mmc_ret = sdrv_mmc_switch_uhs_voltage(host);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error: switch voltage to uhs mode failed\r\n");
return mmc_ret;
}
}
return mmc_ret;
}
/* Function to read SD card information from SD status */
static status_t sdrv_mmc_sd_get_card_ssr(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
static uint8_t raw_sd_status[ROUNDUP(64,
CONFIG_ARCH_CACHE_LINE)] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
struct mmc_command cmd = {0};
uint32_t sd_status[16];
uint32_t *status = sd_status;
uint32_t au_size;
int i;
int j;
mmc_ret = sdrv_mmc_send_app_cmd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed sending App command\r\n");
return mmc_ret;
}
cmd.cmd_index = ACMD13_SEND_SD_STATUS;
cmd.argument = 0x0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
cmd.data.data_ptr = raw_sd_status;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = 0x40;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
memcpy(sd_status, raw_sd_status, sizeof(sd_status));
for (i = 15, j = 0; i >= 0; i--, j++)
sd_status[i] = swap_endian32(sd_status[j]);
au_size = UNPACK_BITS(status, MMC_SD_AU_SIZE_BIT, MMC_SD_AU_SIZE_LEN, 32);
/* Card AU size in sectors */
card->ssr.au_size = 1 << (au_size + 4);
card->ssr.num_aus =
UNPACK_BITS(status, MMC_SD_ERASE_SIZE_BIT, MMC_SD_ERASE_SIZE_LEN, 32);
/* When Erase Time-out Calculation is not supported. set the num_aus 1 */
card->ssr.num_aus = MAX(card->ssr.num_aus, 1);
card->ssr.erase_timeout =
UNPACK_BITS(status, MMC_SD_ERASE_TOUT_BIT, MMC_SD_ERASE_TOUT_LEN, 32);
card->ssr.erase_offset = UNPACK_BITS(status, MMC_SD_ERASE_OFFSET_BIT,
MMC_SD_ERASE_OFFSET_LEN, 32);
return mmc_ret;
}
/* Function to read the SD CARD configuration register */
static uint32_t sdrv_mmc_sd_get_card_scr(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
static uint8_t scr_resp[ROUNDUP(8,
CONFIG_ARCH_CACHE_LINE)] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
struct mmc_command cmd = {0};
uint32_t raw_scr[2];
/* Now read the SCR register */
/* Send APP_CMD before ACMD51*/
mmc_ret = sdrv_mmc_send_app_cmd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed sending App command\r\n");
return mmc_ret;
}
cmd.cmd_index = ACMD51_READ_CARD_SCR;
cmd.argument = 0x0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
cmd.data.data_ptr = scr_resp;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = 0x8;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
memcpy(raw_scr, scr_resp, sizeof(raw_scr));
card->raw_scr[0] = swap_endian32(raw_scr[0]);
card->raw_scr[1] = swap_endian32(raw_scr[1]);
/*
* Parse & Populate the SCR data as per sdcc spec
*/
card->scr.bus_widths =
(card->raw_scr[0] & SD_SCR_BUS_WIDTH_MASK) >> SD_SCR_BUS_WIDTH;
card->scr.cmd23_support = (card->raw_scr[0] & SD_SCR_CMD23_SUPPORT);
card->scr.sd_spec =
(card->raw_scr[0] & SD_SCR_SD_SPEC_MASK) >> SD_SCR_SD_SPEC;
card->scr.sd3_spec =
(card->raw_scr[0] & SD_SCR_SD_SPEC3_MASK) >> SD_SCR_SD_SPEC3;
card->scr.data_stat_after_erase =
(card->raw_scr[0] & SD_SCR_STAT_AFTER_ERASE_MASK) >> SD_SCR_SD_STAT_AFTER_ERASE;
return mmc_ret;
}
/*
* Function: mmc_set_sd_bus_width
* Arg : host, device structure & width
* Return : 0 on Success, 1 on Failure
* Flow : Set the bus width for the card
*/
status_t sdrv_mmc_sd_set_bus_width(struct sdhci_host *host,
struct mmc_card *card,
uint8_t width)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
/* Send APP_CMD before ACMD6*/
mmc_ret = sdrv_mmc_send_app_cmd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed sending App command\r\n");
return mmc_ret;
}
cmd.cmd_index = ACMD6_SET_BUS_WIDTH;
cmd.argument = (width == MMC_DATA_BUS_WIDTH_4BIT) ? (1 << 1) : 0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
return mmc_ret;
}
status_t sdrv_mmc_sd_set_hs(struct sdhci_host *host, struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
static uint8_t switch_resp[ROUNDUP(64,
CONFIG_ARCH_CACHE_LINE)] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
cmd.cmd_index = CMD6_SWITCH_FUNC;
cmd.argument = MMC_SD_SWITCH_HS;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
cmd.data.data_ptr = switch_resp;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = 0x40;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
MMC_SAVE_TIMING(host, SDHCI_UHS_SDR25_MODE);
/* Set the SDR25 mode in controller*/
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_UHS_SDR25_MODE);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_50MHZ);
if (mmc_ret)
return mmc_ret;
}
return mmc_ret;
}
status_t sdrv_mmc_sd_switch(struct sdhci_host *host, struct mmc_card *card,
int mode,
int group, uint8_t value, uint8_t *resp)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
cmd.cmd_index = CMD6_SWITCH_FUNC;
cmd.argument = (mode << 31) | 0xFFFFFF;
cmd.argument &= ~(0xF << (4 * (group - 1)));
cmd.argument |= (value << (4 * (group - 1)));
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
cmd.data.data_ptr = resp;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = 0x40;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(host, &cmd);
if (mmc_ret)
return mmc_ret;
return mmc_ret;
}
status_t sdrv_mmc_sd_init_uhs_sdr25(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
static uint8_t switch_resp[ROUNDUP(64,
CONFIG_ARCH_CACHE_LINE)] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
/* set max current 200mA */
mmc_ret = sdrv_mmc_sd_switch(host, card, 1, 4, 0, switch_resp);
if (mmc_ret)
return mmc_ret;
mmc_ret = sdrv_mmc_sd_switch(host, card, 1, 1, 1, switch_resp);
if (mmc_ret)
return mmc_ret;
MMC_SAVE_TIMING(host, SDHCI_UHS_SDR25_MODE);
/* Set the SDR25 mode in controller*/
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_UHS_SDR25_MODE);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_50MHZ);
if (mmc_ret)
return mmc_ret;
}
return mmc_ret;
}
uint32_t sdrv_mmc_sd_init_uhs_ddr50(struct sdhci_host *host,
struct mmc_card *card)
{
status_t mmc_ret = SDRV_STATUS_OK;
static uint8_t switch_resp[ROUNDUP(64,
CONFIG_ARCH_CACHE_LINE)] __ALIGNED(CONFIG_ARCH_CACHE_LINE);
/* set max current 400mA */
mmc_ret = sdrv_mmc_sd_switch(host, card, 1, 4, 1, switch_resp);
if (mmc_ret)
return mmc_ret;
mmc_ret = sdrv_mmc_sd_switch(host, card, 1, 1, 4, switch_resp);
if (mmc_ret)
return mmc_ret;
MMC_SAVE_TIMING(host, SDHCI_UHS_DDR50_MODE);
/* Set the DDR50 mode in controller*/
if (host->ops->set_uhs_mode)
host->ops->set_uhs_mode(host, SDHCI_UHS_DDR50_MODE);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_50MHZ);
if (mmc_ret)
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc_init_card
* Arg : mmc device structure
* Return : 0 on Success, 1 on Failure
* Flow : Performs initialization and identification of eMMC cards connected
* to the host.
*/
static status_t sdrv_mmc_card_init(struct mmc_device *dev)
{
status_t mmc_ret = SDRV_STATUS_OK;
uint8_t bus_width = 0;
struct sdhci_host *host;
struct mmc_card *card;
struct mmc_config_data *cfg;
host = &dev->host;
card = &dev->card;
cfg = &dev->config;
host->card_type = SDHCI_EMMC_CARD;
/* card hw reset */
if ((host->caps.hw_reset_support) && (host->ops->hw_reset))
host->ops->hw_reset(host);
/* host init */
if (host->ops->priv_init)
host->ops->priv_init(host);
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_400KHZ);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set card clk\r\n");
return mmc_ret;
}
}
/* Initialize MMC card structure */
card->status = MMC_STATUS_INACTIVE;
/* If config 1.8v voltage, set ocr 1.7v-1.9v params, else set 2.7v-3.6v */
card->ocr = MMC_OCR_27_36 | MMC_OCR_SEC_MODE;
if (host->caps.voltage == SDHCI_VOL_1_8)
card->ocr = MMC_OCR_17_19 | MMC_OCR_SEC_MODE;
/* Initialize the internal MMC */
mmc_ret = sdrv_mmc_reset_card_and_send_op(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_ERR, "MMC card failed to respond, try for SD card\r\n");
host->card_type = SDHCI_SD_CARD;
/* host init */
if (host->ops->priv_init)
host->ops->priv_init(host);
/* need reinit clock */
if (host->ops->set_clock) {
mmc_ret = host->ops->set_clock(host, SDHCI_CLK_400KHZ);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set card clk\r\n");
return mmc_ret;
}
}
/* Reset the card & get the OCR */
mmc_ret = sdrv_mmc_sd_card_init(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to initialize SD card\r\n");
return mmc_ret;
}
}
/* Identify (CMD2, CMD3 & CMD9) and select the card (CMD7) */
mmc_ret = sdrv_mmc_identify_card(host, card);
if (mmc_ret)
return mmc_ret;
/* set interface speed */
if (MMC_CARD_SD(card)) {
if (card->type == MMC_CARD_TYPE_UHS) {
if (cfg->bus_width == MMC_BUS_WIDTH_4BIT) {
/* uhs card only support 4bit bus width */
bus_width = MMC_DATA_BUS_WIDTH_4BIT;
}
else {
DETAIL_PRINTF(SSDK_CRIT, "uhs card only support 4bit bus widt\r\n");
return SDRV_STATUS_INVALID_PARAM;
}
mmc_ret = sdrv_mmc_sd_set_bus_width(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set bus width for the card\r\n");
return mmc_ret;
}
/* Set bit bus width in controller */
mmc_ret = sdrv_sdhci_set_bus_width(host, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set bus width for host controller\r\n");
return mmc_ret;
}
if (host->caps.ddr_support) {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in DDR mode\r\n");
mmc_ret = sdrv_mmc_sd_init_uhs_ddr50(host, card);
}
else {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in SDR mode\r\n");
mmc_ret = sdrv_mmc_sd_init_uhs_sdr25(host, card);
}
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set SDR or DDR for SD card\r\n");
return mmc_ret;
}
}
else {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in HS mode\r\n");
mmc_ret = sdrv_mmc_sd_set_hs(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set HS for SD card\r\n");
return mmc_ret;
}
}
}
else {
mmc_ret = sdrv_mmc_set_hs_interface(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error adjusting interface speed!\r\n");
return mmc_ret;
}
}
/* Now get the extended CSD for the card */
if (MMC_CARD_MMC(card)) {
/* For MMC cards, also get the extended csd */
mmc_ret = sdrv_mmc_get_ext_csd(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure getting card's ExtCSD information!\r\n");
return mmc_ret;
}
}
else {
/*Read SCR for sd card */
mmc_ret = sdrv_mmc_sd_get_card_scr(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure getting card's SCR register\r\n");
return mmc_ret;
}
/* Read SSR for the SD card */
mmc_ret = sdrv_mmc_sd_get_card_ssr(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get SSR from the card\r\n");
return mmc_ret;
}
}
/* Decode and save the CSD register */
mmc_ret = sdrv_mmc_decode_and_save_csd(card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure decoding card's CSD information!\r\n");
return mmc_ret;
}
if (MMC_CARD_MMC(card)) {
/* Set the bus width based on host, target capbilities */
if (cfg->bus_width == MMC_BUS_WIDTH_8BIT && host->caps.bus_width_8bit)
bus_width = MMC_DATA_BUS_WIDTH_8BIT;
/*
* Host contoller by default supports 4 bit & 1 bit mode.
* No need to check for host support here
*/
else if (cfg->bus_width == MMC_BUS_WIDTH_4BIT)
bus_width = MMC_DATA_BUS_WIDTH_4BIT;
else
bus_width = MMC_DATA_BUS_WIDTH_1BIT;
/* Set 4/8 bit SDR bus width in controller */
mmc_ret = sdrv_sdhci_set_bus_width(host, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set bus width for host controller\r\n");
return mmc_ret;
}
/*
* Enable high speed mode in the follwing order:
* 1. HS400 mode if supported by host & card
* 1. HS200 mode if supported by host & card
* 2. DDR mode host, if supported by host & card
* 3. Use normal speed mode with supported bus width
*/
if (host->caps.hs400_support && sdrv_mmc_card_supports_hs400_mode(card)) {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in HS400 mode\r\n");
mmc_ret = sdrv_mmc_set_hs400_mode(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set HS400 mode for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
}
#if USE_TARGET_HS200_CAPS
else if (host->caps.hs200_support && host->caps.sdr104_support &&
sdrv_mmc_card_supports_hs200_mode(card))
#else
else if (host->caps.sdr104_support &&
sdrv_mmc_card_supports_hs200_mode(card))
#endif
{
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in HS200 mode\r\n");
mmc_ret = sdrv_mmc_set_hs200_mode(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set HS200 mode for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
}
else if (host->caps.ddr_support && sdrv_mmc_card_supports_ddr_mode(card)) {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in DDR mode\r\n");
mmc_ret = sdrv_mmc_set_ddr_mode(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set DDR mode for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
}
else {
DETAIL_PRINTF(SSDK_INFO, "SDHC Running in High Speed mode\r\n");
/* Set HS_TIMING mode */
mmc_ret = sdrv_mmc_set_hs_interface(host, card);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to enalbe HS mode for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
/* Set wide bus mode */
mmc_ret = sdrv_mmc_set_bus_width(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failure to set wide bus for Card(RCA:%x)\r\n",
card->rca);
return mmc_ret;
}
}
}
else if (card->type != MMC_CARD_TYPE_UHS) {
/* Check the supported bus width for the card from SCR register */
if ((card->scr.bus_widths & SD_SCR_WIDTH_4BIT)
&& (cfg->bus_width == MMC_BUS_WIDTH_4BIT))
bus_width = MMC_DATA_BUS_WIDTH_4BIT;
else
bus_width = MMC_DATA_BUS_WIDTH_1BIT;
mmc_ret = sdrv_mmc_sd_set_bus_width(host, card, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set bus width for the card\r\n");
return mmc_ret;
}
/* Set bit SDR bus width in controller */
mmc_ret = sdrv_sdhci_set_bus_width(host, bus_width);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set bus width for host controller\r\n");
return mmc_ret;
}
}
card->block_size = MMC_BLK_SZ;
#ifdef MMC_ENABLE_RST_N_FUNC
if (MMC_CARD_MMC(card)) {
/* Enable RST_n_FUNCTION */
if (!card->ext_csd[MMC_EXT_CSD_RST_N_FUNC]) {
mmc_ret =
sdrv_mmc_switch_cmd(host, card, MMC_SET_BIT, MMC_EXT_CSD_RST_N_FUNC,
RST_N_FUNC_ENABLE);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to enable RST_n_FUNCTION\r\n");
return mmc_ret;
}
}
}
#endif
if (MMC_CARD_MMC(card) && dev->card.ext_csd[MMC_EXT_CSD_REV] >= 6) {
mmc_ret = sdrv_mmc_switch_cmd(&dev->host, &dev->card, MMC_ACCESS_WRITE,
MMC_POWER_OFF_NOTIFICATION, MMC_POWER_ON);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to power off notification MMC_POWER_ON\r\n");
return mmc_ret;
}
card->ext_csd[MMC_POWER_OFF_NOTIFICATION] = MMC_POWER_ON;
}
return mmc_ret;
}
/*
* Function: mmc display csd
* Arg : None
* Return : None
* Flow : Displays the csd information
*/
static void sdrv_mmc_display_csd(struct mmc_card *card)
{
ssdk_printf(SSDK_INFO, "erase_grpsize: %d\r\n", card->csd.erase_grp_size);
ssdk_printf(SSDK_INFO, "erase_grpmult: %d\r\n", card->csd.erase_grp_mult);
ssdk_printf(SSDK_INFO, "wp_grpsize: %d\r\n", card->csd.wp_grp_size);
ssdk_printf(SSDK_INFO, "wp_grpen: %d\r\n", card->csd.wp_grp_enable);
ssdk_printf(SSDK_INFO, "perm_wp: %d\r\n", card->csd.perm_wp);
ssdk_printf(SSDK_INFO, "temp_wp: %d\r\n", card->csd.temp_wp);
}
uint32_t sdrv_mmc_get_erase_group_size(struct mmc_device *dev)
{
struct mmc_csd *mmc_csd = &dev->card.csd;
if (dev->card.type < MMC_TYPE_MMCHC) {
DETAIL_PRINTF(SSDK_INFO, "\r\n sd card au_size %d, num_aus %d\r\n",
dev->card.ssr.au_size, dev->card.ssr.num_aus);
if (mmc_csd->cmmc_structure == 1) {
/* CSD Version 2.0 */
dev->erase_grp_size = MMC_BLK_SZ;
}
else {
/* CSD Version 1.0 */
if (mmc_csd->erase_blk_len)
dev->erase_grp_size = MMC_BLK_SZ;
else
dev->erase_grp_size = mmc_csd->sector_size * MMC_BLK_SZ;
}
}
else {
DETAIL_PRINTF(SSDK_INFO, "\r\n mmc card erase_size %d, erase_mult %d\r\n",
dev->card.csd.erase_grp_size, dev->card.csd.erase_grp_mult);
dev->erase_grp_size = (dev->card.csd.erase_grp_size + 1) *
(dev->card.csd.erase_grp_mult + 1) * MMC_BLK_SZ;
}
return dev->erase_grp_size;
}
uint64_t sdrv_mmc_get_size(struct mmc_device *dev, uint32_t type)
{
struct mmc_card *card = &dev->card;
if (MMC_CARD_MMC(card)) {
switch (type) {
case PART_ACCESS_DEFAULT:
return card->capacity;
break;
case PART_ACCESS_BOOT1:
return card->boot_size;
break;
case PART_ACCESS_BOOT2:
return card->boot_size;
break;
case PART_ACCESS_RPMB:
return card->rpmb_size;
break;
case PART_ACCESS_GP1:
return 0;
break;
case PART_ACCESS_GP2:
return 0;
break;
case PART_ACCESS_GP3:
return 0;
break;
case PART_ACCESS_GP4:
return 0;
break;
default:
return 0;
}
}
else if (MMC_CARD_SD(card)) {
switch (type) {
case PART_ACCESS_DEFAULT:
return card->capacity;
break;
default:
return 0;
}
}
else
return 0;
}
uint32_t sdrv_mmc_get_block_size(struct mmc_device *dev)
{
struct mmc_card *card = &dev->card;
return card->block_size;
}
uint32_t sdrv_mmc_get_data_after_erase(struct mmc_device *dev)
{
struct mmc_card *card = &dev->card;
return MMC_CARD_SD(card) ? card->scr.data_stat_after_erase : 0;
}
/*
* Function: sdrv_mmc_alloc_dev
* Arg : None
* Return : Pointer to mmc device
* Flow : Alloc mmc device memory
*/
struct mmc_device *sdrv_mmc_alloc_dev(void)
{
struct mmc_device *dev;
dev = (struct mmc_device *)malloc(sizeof(struct mmc_device));
if (!dev) {
DETAIL_PRINTF(SSDK_CRIT, "Error allocating mmc device\r\n");
return NULL;
}
memset(dev, 0, sizeof(struct mmc_device));
return dev;
}
/*
* Function: sdrv_mmc_sdrv_sdhci_init
* Arg : mmc device structure
* MMC configuration data
* Return : 0 on Success, 1 on Failure
* Flow : Entry point to MMC boot process
* Initialize the sd host controller
* Initialize the mmc card
* Set the clock & high speed mode
*/
status_t sdrv_mmc_sdrv_sdhci_init(struct mmc_device *dev)
{
status_t mmc_ret = SDRV_STATUS_OK;
ASSERT(dev);
memset(&dev->card, 0, sizeof(struct mmc_card));
dev->part_curr = PART_ACCESS_DEFAULT;
/* Initialize the host & clock */
DETAIL_PRINTF(SSDK_INFO,
" Initializing MMC host data structure and clock!\r\n");
mmc_ret = sdrv_mmc_host_init(dev);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error Initializing MMC host : %u\r\n", mmc_ret);
return mmc_ret;
}
/* Initialize and identify cards connected to host */
mmc_ret = sdrv_mmc_card_init(dev);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed detecting MMC/SDC @ slot%d\r\n",
dev->config.slot);
return mmc_ret;
}
DETAIL_PRINTF(SSDK_INFO, "Done initialization of the card\r\n");
sdrv_mmc_display_csd(&dev->card);
return mmc_ret;
}
static status_t sdrv_mmc_parse_response(uint32_t resp)
{
status_t mmc_ret = SDRV_STATUS_OK;
/* Trying to write beyond card capacity */
if (resp & MMC_R1_ADDR_OUT_OF_RANGE) {
DETAIL_PRINTF(SSDK_CRIT,
"Attempting to read or write beyond the Device capacity\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Misaligned address not matching block length */
if (resp & MMC_R1_ADDR_ERR) {
DETAIL_PRINTF(SSDK_CRIT,
"The misaligned address did not match the block length used\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Invalid block length */
if (resp & MMC_R1_BLOCK_LEN_ERR) {
DETAIL_PRINTF(SSDK_CRIT,
"The transferred bytes does not match the block length\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Tried to program write protected block */
if (resp & MMC_R1_WP_VIOLATION) {
DETAIL_PRINTF(SSDK_CRIT, "Attempt to program a write protected block\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* card controller error */
if (resp & MMC_R1_CC_ERROR) {
DETAIL_PRINTF(SSDK_CRIT,
"Device error occurred, which is not related to the host command\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Generic error */
if (resp & MMC_R1_GENERIC_ERR) {
DETAIL_PRINTF(SSDK_CRIT, "A generic Device error\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Finally check for card in TRAN state */
if (MMC_CARD_STATUS(resp) != MMC_TRAN_STATE) {
DETAIL_PRINTF(SSDK_CRIT, "MMC card is not in TRAN state\r\n");
mmc_ret = SDRV_STATUS_MMC_CMD_RSP_ERR;
}
return mmc_ret;
}
static status_t sdrv_mmc_stop_command(struct mmc_device *dev)
{
struct mmc_command cmd;
status_t mmc_ret = SDRV_STATUS_OK;
memset(&cmd, 0, sizeof(struct mmc_command));
if (dev->host.card_type == SDHCI_SDIO_CARD)
cmd.cmd_index = CMD52_SDIO_STOP_TRANSMISSION;
else
cmd.cmd_index = CMD12_STOP_TRANSMISSION;
cmd.argument = (dev->card.rca << 16) | BIT(0);
cmd.cmd_type = SDHCI_CMD_TYPE_ABORT;
cmd.resp_type = SDHCI_CMD_RESP_R1;
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to send stop command\r\n");
return mmc_ret;
}
/* Response contains 32 bit Card status.
* Parse the errors & provide relevant information */
return sdrv_mmc_parse_response(cmd.resp[0]);
}
static uint32_t sdrv_mmc_set_data_timeout(struct mmc_device *dev,
struct mmc_command *cmd)
{
struct sdhci_host *host = &dev->host;
struct mmc_card *card = &dev->card;
uint32_t mult = 1;
uint32_t timeout_ns;
uint32_t timeout_clks;
if (!cmd->data_present)
return 0;
/*
* SDIO cards only define an upper 1 s limit on access.
*/
if (MMC_CARD_SDIO(card))
cmd->cmd_timeout = 1000000;
/*
* SD cards use a 100 multiplier rather than 10
*/
mult = MMC_CARD_SD(card) ? 100 : 10;
/*
* Scale up the multiplier (and therefore the timeout) by
* the r2w factor for writes.
*/
if (cmd->trans_mode == SDHCI_MMC_WRITE)
mult <<= card->csd.r2w_factor;
timeout_ns = card->csd.taac_ns * mult;
timeout_clks = card->csd.taac_clks * mult;
/*
* SD cards also have an upper limit on the timeout.
*/
if (MMC_CARD_SD(card)) {
uint32_t timeout_us, limit_us;
timeout_us = timeout_ns / 1000;
if (host->cur_clk_rate)
timeout_us += timeout_clks * 1000 /
(host->cur_clk_rate / 1000);
if (cmd->trans_mode == SDHCI_MMC_WRITE)
/*
* The MMC spec "It is strongly recommended
* for hosts to implement more than 500ms
* timeout value even if the card indicates
* the 250ms maximum busy length." Even the
* previous value of 300ms is known to be
* insufficient for some cards.
*/
limit_us = 3000000;
else
limit_us = 100000;
/*
* SDHC cards always use these fixed values.
*/
if (timeout_us > limit_us) {
timeout_ns = limit_us * 1000;
timeout_clks = 0;
}
/* assign limit value if invalid */
if (timeout_us == 0)
timeout_ns = limit_us * 1000;
}
cmd->cmd_timeout = timeout_ns / 1000;
return 0;
}
/*
* Function: actual mmc sdhci read
* Arg : mmc device structure, block address, number of blocks & destination
* Return : 0 on Success, non zero on success
* Flow : Fill in the command structure & send the command
*/
static status_t sdrv_mmc_send_read(struct mmc_device *dev, void *dest,
uint32_t blk_addr,
uint32_t num_blocks)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
uint32_t status;
uint32_t retry = 0;
struct mmc_card *card = &dev->card;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD17/18 Format:
* [31:0] Data Address
*/
if (num_blocks == 1)
cmd.cmd_index = CMD17_READ_SINGLE_BLOCK;
else
cmd.cmd_index = CMD18_READ_MULTIPLE_BLOCK;
/*
* Standard emmc cards use byte mode addressing
* convert the block address to byte address before
* sending the command
*/
if (card->type == MMC_TYPE_STD_MMC)
cmd.argument = blk_addr * card->block_size;
else
cmd.argument = blk_addr;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
/* Use CMD23 If card supports CMD23:
* For SD card use the value read from SCR register
* For emmc by default use CMD23.
* Also as per SDCC spec always use CMD23 to stop
* multiblock read/write if UHS (Ultra High Speed) is
* enabled
*/
if (MMC_CARD_SD(card))
cmd.cmd23_support = dev->card.scr.cmd23_support;
else
cmd.cmd23_support = 0x1;
cmd.data.data_ptr = dest;
cmd.data.num_blocks = num_blocks;
cmd.retries = MMC_CMD_RETRIES;
/* config cmd data hw NAC timeout.
* delay between the end bit of the read command
* and the start bit of the data block.
* interrupt mode, hw timeout valid.
*/
sdrv_mmc_set_data_timeout(dev, &cmd);
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
/* For multi block read failures send stop command */
if (mmc_ret && num_blocks > 1) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to send read cmd\n");
return mmc_ret;
}
/*
* Response contains 32 bit Card status.
* Parse the errors & provide relevant information
*/
mmc_ret = sdrv_mmc_parse_response(cmd.resp[0]);
if (mmc_ret)
return mmc_ret;
while (1) {
mmc_ret = sdrv_mmc_get_card_status(&dev->host, &dev->card, &status);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get card status after read\r\n");
return mmc_ret;
}
if ((status & MMC_READY_FOR_DATA) &&
(MMC_CARD_STATUS(status) == MMC_TRAN_STATE))
break;
retry++;
udelay(1000);
if (retry == MMC_MAX_CARD_STAT_RETRY) {
DETAIL_PRINTF(SSDK_CRIT,
"Card status check timed out after sending read command\r\n");
return SDRV_STATUS_TIMEOUT;
}
}
return mmc_ret;
}
/*
* Function: mmc sdhci read
* Arg : mmc device structure, block address, number of blocks & destination
* Return : 0 on Success, non zero on success
* Flow : Fill in the command structure & send the command
*/
status_t sdrv_mmc_sdhci_read(struct mmc_device *dev, void *dest,
uint32_t blk_addr,
uint32_t num_blocks)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct sdhci_host *host = &dev->host;
uint32_t desc_len_max = 0;
uint32_t blocks_num_max = 0;
uint32_t rblocks_num = 0;
uint64_t dest_offset = 0;
uint64_t addr_offset = 0;
if (host->caps.addr_64bit_v4) {
desc_len_max = SDHCI_ADMA2_DESC_LINE_SZ_V4 * (ADMA_DESC_LIST_SIZE - 1);
}
else {
desc_len_max = SDHCI_ADMA_DESC_LINE_SZ * (ADMA_DESC_LIST_SIZE - 1);
}
blocks_num_max = desc_len_max / SDHCI_MMC_BLK_SZ;
blocks_num_max = MIN(blocks_num_max, MMC_BLOCK_COUNT_MASK);
while (num_blocks) {
rblocks_num = MIN(blocks_num_max, num_blocks);
mmc_ret = sdrv_mmc_send_read(dev, (uint8_t *)dest + dest_offset,
blk_addr + addr_offset, rblocks_num);
if (mmc_ret)
return mmc_ret;
num_blocks -= rblocks_num;
addr_offset += rblocks_num;
dest_offset += rblocks_num * SDHCI_MMC_BLK_SZ;
}
return 0;
}
/*
* Function: actual mmc sdhci write
* Arg : mmc device structure, block address, number of blocks & source
* Return : 0 on Success, non zero on success
* Flow : Fill in the command structure & send the command
*/
static status_t sdrv_mmc_send_write(struct mmc_device *dev, void *src,
uint32_t blk_addr,
uint32_t num_blocks)
{
status_t mmc_ret = SDRV_STATUS_OK;
uint32_t status;
uint32_t retry = 0;
struct mmc_command cmd;
struct mmc_card *card = &dev->card;
memset(&cmd, 0, sizeof(struct mmc_command));
/* CMD24/25 Format:
* [31:0] Data Address
*/
if (num_blocks == 1)
cmd.cmd_index = CMD24_WRITE_SINGLE_BLOCK;
else
cmd.cmd_index = CMD25_WRITE_MULTIPLE_BLOCK;
/*
* Standard emmc cards use byte mode addressing
* convert the block address to byte address before
* sending the command
*/
if (card->type == MMC_TYPE_STD_MMC)
cmd.argument = blk_addr * card->block_size;
else
cmd.argument = blk_addr;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_WRITE;
/* Use CMD23 If card supports CMD23:
* For SD card use the value read from SCR register
* For emmc by default use CMD23.
* Also as per SDCC spec always use CMD23 to stop
* multiblock read/write if UHS (Ultra High Speed) is
* enabled
*/
if (MMC_CARD_SD(card))
cmd.cmd23_support = dev->card.scr.cmd23_support;
else
cmd.cmd23_support = 0x1;
cmd.data_present = 0x1;
cmd.data.data_ptr = src;
cmd.data.num_blocks = num_blocks;
cmd.retries = MMC_CMD_RETRIES;
/* config cmd data hw NAC timeout.
* delay between the end bit of the read command
* and the start bit of the data block.
* interrupt mode, hw timeout valid.
*/
sdrv_mmc_set_data_timeout(dev, &cmd);
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
/* For multi block write failures send stop command */
if (mmc_ret && num_blocks > 1) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to send write cmd\n");
return mmc_ret;
}
/*
* Response contains 32 bit Card status.
* Parse the errors & provide relevant information
*/
mmc_ret = sdrv_mmc_parse_response(cmd.resp[0]);
if (mmc_ret)
return mmc_ret;
while (1) {
mmc_ret = sdrv_mmc_get_card_status(&dev->host, &dev->card, &status);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get card status after write\r\n");
return mmc_ret;
}
if ((status & MMC_READY_FOR_DATA) &&
(MMC_CARD_STATUS(status) == MMC_TRAN_STATE))
break;
retry++;
udelay(1000);
if (retry == MMC_MAX_CARD_STAT_RETRY) {
DETAIL_PRINTF(SSDK_CRIT,
"Card status check timed out after sending write command\r\n");
return SDRV_STATUS_TIMEOUT;
}
}
return mmc_ret;
}
/*
* Function: mmc sdhci write
* Arg : mmc device structure, block address, number of blocks & source
* Return : 0 on Success, non zero on success
* Flow : Fill in the command structure & send the command
*/
status_t sdrv_mmc_sdhci_write(struct mmc_device *dev, void *src,
uint32_t blk_addr,
uint32_t num_blocks)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct sdhci_host *host = &dev->host;
uint32_t desc_len_max = 0;
uint32_t blocks_num_max = 0;
uint32_t rblocks_num = 0;
uint64_t src_offset = 0;
uint64_t addr_offset = 0;
if (host->caps.addr_64bit_v4) {
desc_len_max = SDHCI_ADMA2_DESC_LINE_SZ_V4 * (ADMA_DESC_LIST_SIZE - 1);
}
else {
desc_len_max = SDHCI_ADMA_DESC_LINE_SZ * (ADMA_DESC_LIST_SIZE - 1);
}
blocks_num_max = desc_len_max / SDHCI_MMC_BLK_SZ;
blocks_num_max = MIN(blocks_num_max, MMC_BLOCK_COUNT_MASK);
while (num_blocks) {
rblocks_num = MIN(blocks_num_max, num_blocks);
mmc_ret = sdrv_mmc_send_write(dev, (uint8_t *)src + src_offset,
blk_addr + addr_offset, rblocks_num);
if (mmc_ret)
return mmc_ret;
num_blocks -= rblocks_num;
addr_offset += rblocks_num;
src_offset += rblocks_num * SDHCI_MMC_BLK_SZ;
}
return mmc_ret;
}
/*
* Send the erase group start address using CMD35
*/
static status_t sdrv_mmc_send_erase_grp_start(struct mmc_device *dev,
uint32_t erase_start)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
struct mmc_card *card = &dev->card;
memset(&cmd, 0, sizeof(struct mmc_command));
if (MMC_CARD_MMC(card))
cmd.cmd_index = CMD35_ERASE_GROUP_START;
else
cmd.cmd_index = CMD32_ERASE_WR_BLK_START;
/*
* Standard emmc cards use byte mode addressing
* convert the block address to byte address before
* sending the command
*/
if (card->type == MMC_TYPE_STD_MMC)
cmd.argument = erase_start * card->block_size;
else
cmd.argument = erase_start;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret)
return mmc_ret;
/*
* CMD35 on failure returns address out of range error
*/
if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0])) {
DETAIL_PRINTF(SSDK_CRIT, "Address for CMD35 is out of range\r\n");
return SDRV_STATUS_MMC_CMD_RSP_ERR;
}
return mmc_ret;
}
/*
* Send the erase group end address using CMD36
*/
static status_t sdrv_mmc_send_erase_grp_end(struct mmc_device *dev,
uint32_t erase_end)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
struct mmc_card *card = &dev->card;
memset(&cmd, 0, sizeof(struct mmc_command));
if (MMC_CARD_MMC(card))
cmd.cmd_index = CMD36_ERASE_GROUP_END;
else
cmd.cmd_index = CMD33_ERASE_WR_BLK_END;
/*
* Standard emmc cards use byte mode addressing
* convert the block address to byte address before
* sending the command
*/
if (card->type == MMC_TYPE_STD_MMC)
cmd.argument = erase_end * card->block_size;
else
cmd.argument = erase_end;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret)
return mmc_ret;
/*
* CMD3 on failure returns address out of range error
*/
if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0])) {
DETAIL_PRINTF(SSDK_CRIT, "Address for CMD36 is out of range\r\n");
return SDRV_STATUS_MMC_CMD_RSP_ERR;
}
return mmc_ret;
}
/*
* Send the erase CMD38, to erase the selected erase groups
*/
static status_t sdrv_mmc_send_erase(struct mmc_device *dev,
uint64_t erase_timeout)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
uint32_t status;
uint32_t retry = 0;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.cmd_index = CMD38_ERASE;
cmd.argument = 0x00000000;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1B;
cmd.cmd_timeout = erase_timeout;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret)
return mmc_ret;
do {
mmc_ret = sdrv_mmc_get_card_status(&dev->host, &dev->card, &status);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get card status after erase\r\n");
return mmc_ret;
}
/* Check if the response of erase command has eras skip status set */
if (status & MMC_R1_WP_ERASE_SKIP)
DETAIL_PRINTF(SSDK_CRIT,
"Write Protect set for the region, only partial space was erased\r\n");
retry++;
udelay(1000);
if (retry == MMC_MAX_CARD_STAT_RETRY) {
DETAIL_PRINTF(SSDK_CRIT,
"Card status check timed out after sending erase command\r\n");
return SDRV_STATUS_TIMEOUT;
}
}
while (!(status & MMC_READY_FOR_DATA) ||
(MMC_CARD_STATUS(status) == MMC_PROG_STATE));
return mmc_ret;
}
/*
* Function: mmc sdhci erase
* Arg : mmc device structure, block address and length
* Return : 0 on Success, non zero on failure
* Flow : Fill in the command structure & send the command
*/
status_t sdrv_mmc_sdhci_erase(struct mmc_device *dev, uint32_t blk_addr,
uint64_t len)
{
status_t mmc_ret = SDRV_STATUS_OK;
uint32_t erase_unit_sz = 0;
uint32_t erase_start;
uint32_t erase_end;
uint32_t blk_end;
uint32_t num_erase_grps;
uint64_t erase_timeout = 0;
struct mmc_card *card;
card = &dev->card;
/*
* Calculate the erase unit size,
* 1. Based on emmc 4.5 spec for emmc card
* 2. Use SD Card Status info for SD cards
*/
if (MMC_CARD_MMC(card)) {
/*
* Calculate the erase unit size as per the emmc specification v4.5
*/
if (dev->card.ext_csd[MMC_ERASE_GRP_DEF])
erase_unit_sz =
(MMC_HC_ERASE_MULT * dev->card.ext_csd[MMC_HC_ERASE_GRP_SIZE]) /
MMC_BLK_SZ;
else
erase_unit_sz = (dev->card.csd.erase_grp_size + 1) *
(dev->card.csd.erase_grp_mult + 1);
}
else {
struct mmc_csd *mmc_csd = &card->csd;
if (mmc_csd->cmmc_structure == 1) {
/* CSD Version 2.0 */
erase_unit_sz = 1;
}
else {
/* CSD Version 1.0 */
if (mmc_csd->erase_blk_len)
erase_unit_sz = 1;
else
erase_unit_sz = mmc_csd->sector_size;
}
}
uint32_t num_blocks = len / MMC_BLK_SZ;
if (num_blocks < erase_unit_sz) {
DETAIL_PRINTF(SSDK_CRIT,
"Requested length is less than min erase group size\r\n");
return SDRV_STATUS_INVALID_PARAM;
}
/* Calculate erase groups based on the length in blocks */
num_erase_grps = num_blocks / erase_unit_sz;
/* Start address of the erase range */
erase_start = blk_addr;
if (MMC_CARD_MMC(card))
/* Last address of the erase range */
erase_end = blk_addr + ((num_erase_grps - 1) * erase_unit_sz);
else
erase_end = blk_addr + num_blocks - 1;
/* Boundary check for overlap */
blk_end = blk_addr + num_blocks;
if (erase_end > blk_end) {
DETAIL_PRINTF(SSDK_CRIT,
"The erase group overlaps the max requested for erase\r\n");
erase_end -= erase_unit_sz;
}
/* Send CMD35 for erase group start */
mmc_ret = sdrv_mmc_send_erase_grp_start(dev, erase_start);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to send erase grp start address\r\n");
return mmc_ret;
}
/* Send CMD36 for erase group end */
mmc_ret = sdrv_mmc_send_erase_grp_end(dev, erase_end);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to send erase grp end address\r\n");
return mmc_ret;
}
if (MMC_CARD_MMC(card)) {
/*
* As per emmc 4.5 spec section 7.4.27, calculate the erase timeout
* erase_timeout = 300ms * ERASE_TIMEOUT_MULT * num_erase_grps
*/
erase_timeout = (300 * 1000 * card->ext_csd[MMC_ERASE_TIMEOUT_MULT] *
num_erase_grps);
}
else {
/*
* For sd card, calculate the erase timeout
* erase_timeout = (earse_timeout * num_erase_grps + erase_offset) * 1s
*/
erase_timeout =
card->ssr.erase_timeout * num_erase_grps + card->ssr.erase_offset;
erase_timeout = MAX(erase_timeout, 1);
erase_timeout *= 1000 * 1000;
DETAIL_PRINTF(SSDK_DEBUG,
"\r\n ssr erase_timeout %d, num_erase_grps %d, ssr erase_offset %d\r\n",
card->ssr.erase_timeout, num_erase_grps, card->ssr.erase_offset);
}
/* Send CMD38 to perform erase */
mmc_ret = sdrv_mmc_send_erase(dev, erase_timeout);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to erase the specified partition\r\n");
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc sdhci cancel
* Arg : mmc device structure
* Return : 0 on Success, non zero on failure
* Flow : call sdrv_mmc_stop_command
*/
status_t sdrv_mmc_sdhci_cancel(struct mmc_device *dev)
{
return sdrv_mmc_stop_command(dev);
}
/*
* Function: mmc get wp status
* Arg : mmc device structure, block address and buffer for getting wp
* status Return : 0 on Success, 1 on Failure Flow : Get the WP group status
* by sending CMD31
*/
status_t sdrv_mmc_get_wp_status(struct mmc_device *dev, uint32_t addr,
uint8_t *wp_status)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.cmd_index = CMD31_SEND_WRITE_PROT_TYPE;
cmd.argument = addr;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x1;
cmd.data.data_ptr = wp_status;
cmd.data.num_blocks = 0x1;
cmd.data.blk_sz = 0x8;
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get status of write protect bits\r\n");
return mmc_ret;
}
return mmc_ret;
}
/*
* Function: mmc set/clear WP on user area
* Arg : mmc device structure, block address,len, & flag to set or clear
* Return : 0 on success, 1 on failure
* Flow : Function to set/clear power on write protect on user area
*/
status_t sdrv_mmc_set_clr_power_on_wp_user(struct mmc_device *dev,
uint32_t addr,
uint64_t len, uint8_t set_clr)
{
struct mmc_command cmd;
struct mmc_card *card = &dev->card;
uint32_t wp_grp_size;
uint32_t status;
uint32_t num_wp_grps;
status_t ret = SDRV_STATUS_OK;
uint32_t retry = 0;
uint32_t i;
memset(&cmd, 0, sizeof(struct mmc_command));
/* Convert len into blocks */
len = len / MMC_BLK_SZ;
wp_grp_size = dev->card.wp_grp_size;
/* Disable PERM WP */
ret = sdrv_mmc_switch_cmd(&dev->host, &dev->card, MMC_SET_BIT, MMC_USR_WP,
MMC_US_PERM_WP_DIS);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to Disable PERM WP\r\n");
return ret;
}
/* Read the default values for user WP */
ret = sdrv_mmc_get_ext_csd(&dev->host, &dev->card);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to read ext csd for the card\r\n");
return ret;
}
/* Check if user power on WP is disabled or perm WP is enabled */
if ((dev->card.ext_csd[MMC_USR_WP] & MMC_US_PWR_WP_DIS) ||
(dev->card.ext_csd[MMC_USR_WP] & MMC_US_PERM_WP_EN)) {
DETAIL_PRINTF(SSDK_CRIT, "Power on protection is disabled, cannot be set\r\n");
return SDRV_STATUS_FAIL;
}
if (len < wp_grp_size) {
DETAIL_PRINTF(SSDK_CRIT, "Length is less than min WP size, WP was not set\r\n");
return SDRV_STATUS_FAIL;
}
/* Set power on USER WP */
ret = sdrv_mmc_switch_cmd(&dev->host, &dev->card, MMC_SET_BIT, MMC_USR_WP,
MMC_US_PWR_WP_EN);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to set power on WP for user\r\n");
return ret;
}
num_wp_grps = ROUNDUP(len, wp_grp_size) / wp_grp_size;
if (set_clr)
cmd.cmd_index = CMD28_SET_WRITE_PROTECT;
else
cmd.cmd_index = CMD29_CLEAR_WRITE_PROTECT;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1B;
for (i = 0; i < num_wp_grps; i++) {
/*
* Standard emmc cards use byte mode addressing
* convert the block address to byte address before
* sending the command
*/
if (card->type == MMC_TYPE_STD_MMC)
cmd.argument = (addr + (i * wp_grp_size)) * card->block_size;
else
cmd.argument = addr + (i * wp_grp_size);
ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (ret)
return ret;
/* CMD28/CMD29 On failure returns address out of range error */
if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0])) {
DETAIL_PRINTF(SSDK_CRIT, "Address for CMD28/29 is out of range\r\n");
return SDRV_STATUS_MMC_CMD_RSP_ERR;
}
/* Check the card status */
do {
ret = sdrv_mmc_get_card_status(&dev->host, &dev->card, &status);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT,
"Failed to get card status afterapplying write protect\r\n");
return ret;
}
/* Time out for WP command */
retry++;
udelay(1000);
if (retry == MMC_MAX_CARD_STAT_RETRY) {
DETAIL_PRINTF(SSDK_CRIT,
"Card status timed out after sending write protect command\r\n");
return SDRV_STATUS_TIMEOUT;
}
}
while (!(status & MMC_READY_FOR_DATA) ||
(MMC_CARD_STATUS(status) == MMC_PROG_STATE));
}
return ret;
}
/* Function to put the mmc card to sleep */
void sdrv_mmc_put_card_to_sleep(struct mmc_device *dev)
{
struct mmc_command cmd = {0};
struct mmc_card *card = &dev->card;
cmd.cmd_index = CMD7_SELECT_DESELECT_CARD;
cmd.argument = 0x00000000;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_NONE;
cmd.retries = MMC_CMD_RETRIES;
/* send command */
if (sdrv_sdhci_send_command(&dev->host, &cmd)) {
DETAIL_PRINTF(SSDK_CRIT, "card deselect error: %s\r\n", __func__);
return;
}
cmd.cmd_index = CMD5_SLEEP_AWAKE;
cmd.argument = (card->rca << MMC_CARD_RCA_BIT) | MMC_CARD_SLEEP;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1B;
/* send command */
if (sdrv_sdhci_send_command(&dev->host, &cmd))
DETAIL_PRINTF(SSDK_CRIT, "card sleep error: %s\r\n", __func__);
}
static status_t sdrv_mmc_can_poweroff_notify(struct mmc_device *dev)
{
struct mmc_card *card;
card = &dev->card;
return MMC_CARD_MMC(card)
&& card->ext_csd[MMC_POWER_OFF_NOTIFICATION] == MMC_POWER_ON;
}
/* Function to power off Notification mmc card */
status_t sdrv_mmc_power_off_notification(struct mmc_device *dev,
uint32_t pon_type)
{
status_t ret = SDRV_STATUS_OK;
ret = sdrv_mmc_can_poweroff_notify(dev);
if (ret == false) {
DETAIL_PRINTF(SSDK_CRIT, "emmc not support power off notification\r\n");
return SDRV_STATUS_FAIL;
}
ret = sdrv_mmc_switch_cmd(&dev->host, &dev->card, MMC_ACCESS_WRITE,
MMC_POWER_OFF_NOTIFICATION, pon_type);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to power off notification: %u\r\n", pon_type);
return ret;
}
return ret;
}
/*
* Switch the partition access type to rpmb or default
*/
status_t sdrv_mmc_sdhci_switch_part(struct mmc_device *dev, uint32_t type)
{
uint32_t part_access;
status_t ret = SDRV_STATUS_OK;
struct mmc_card *card;
card = &dev->card;
if (!MMC_CARD_MMC(card))
return SDRV_STATUS_MMC_DEV_TYPE_ERR;
/* Clear the partition access */
part_access =
dev->card.ext_csd[MMC_PARTITION_CONFIG] & ~PARTITION_ACCESS_MASK;
part_access |= type;
ret = sdrv_mmc_switch_cmd(&dev->host, &dev->card, MMC_ACCESS_WRITE,
MMC_PARTITION_CONFIG, part_access);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to switch partition to type: %u\r\n", type);
return ret;
}
dev->card.ext_csd[MMC_PARTITION_CONFIG] = part_access;
return ret;
}
static status_t sdrv_mmc_sdhci_set_blk_cnt(struct mmc_device *dev,
uint32_t blk_cnt,
uint32_t rel_write)
{
status_t mmc_ret = SDRV_STATUS_OK;
struct mmc_command cmd = {0};
cmd.cmd_index = CMD23_SET_BLOCK_COUNT;
cmd.argument = blk_cnt & MMC_BLOCK_COUNT_MASK;
cmd.argument |= rel_write;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.retries = MMC_CMD_RETRIES;
mmc_ret = sdrv_sdhci_send_command(&dev->host, &cmd);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "Set block count failed: %s\r\n", __func__);
return mmc_ret;
}
return mmc_ret;
}
status_t sdrv_mmc_sdhci_rpmb_send(struct mmc_device *dev,
struct mmc_command *cmd)
{
int i;
uint32_t retry = 5;
uint32_t status;
uint32_t rel_write = 0;
status_t ret = SDRV_STATUS_OK;
ASSERT(cmd);
/* 1. Set the partition type to rpmb */
ret = sdrv_mmc_sdhci_switch_part(dev, PART_ACCESS_RPMB);
if (ret)
return ret;
for (i = 0; i < MAX_RPMB_CMDS; i++) {
if (!cmd[i].cmd_index)
break;
if (cmd[i].write_flag == true)
rel_write = BIT(31);
else
rel_write = 0;
/* 2. Set the block count using cmd23 */
ret = sdrv_mmc_sdhci_set_blk_cnt(dev, cmd[i].data.num_blocks, rel_write);
if (ret)
goto err;
/* 3. Send the command */
ret = sdrv_sdhci_send_command(&dev->host, &cmd[i]);
if (ret)
goto err;
do {
/* 4. Poll for card status to ensure rpmb operation completeness */
ret = sdrv_mmc_get_card_status(&dev->host, &dev->card, &status);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Failed to get card status after rpmb operations\r\n");
goto err;
}
retry--;
udelay(500);
if (!retry) {
DETAIL_PRINTF(SSDK_CRIT,
"Card status check timed out after rpmb operations\r\n");
ret = SDRV_STATUS_TIMEOUT;
goto err;
}
}
while (!(status & MMC_READY_FOR_DATA) ||
(MMC_CARD_STATUS(status) == MMC_PROG_STATE));
}
/* If we reach here, that means success */
ret = SDRV_STATUS_OK;
err:
/* 5. Switch the partition back to default type */
if (sdrv_mmc_sdhci_switch_part(dev, PART_ACCESS_DEFAULT))
return ret;
return ret;
}
status_t sdrv_mmc_sdhci_read_part(struct mmc_device *dev, void *dest,
uint32_t blk_addr,
uint32_t num_blocks, uint32_t type)
{
status_t mmc_ret = SDRV_STATUS_OK;
if ((NULL == dev) || (NULL == dest)) {
DETAIL_PRINTF(SSDK_CRIT, "The passed pointer is null\n");
return SDRV_STATUS_INVALID_PARAM;
}
struct mmc_card *card = &dev->card;
uint64_t mmc_size = sdrv_mmc_get_size(dev, type);
if (mmc_size == 0) {
DETAIL_PRINTF(SSDK_CRIT, "mmc part %d not support\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
if (!IS_ALIGNED(dest, CONFIG_ARCH_CACHE_LINE)) {
DETAIL_PRINTF(SSDK_CRIT, "dest address %p not aligned to cache line %d\n", dest,
CONFIG_ARCH_CACHE_LINE);
return SDRV_STATUS_INVALID_PARAM;
}
if (num_blocks == 0
|| ((blk_addr + num_blocks) > (mmc_size / card->block_size))) {
DETAIL_PRINTF(SSDK_CRIT,
"num_blocks == 0 or blk_addr + num_blocks out of the mmc size range\n");
DETAIL_PRINTF(SSDK_CRIT,
"blk_addr:%x num_blocks:%x mmc block size:%llx\n", blk_addr, num_blocks,
(mmc_size / card->block_size));
return SDRV_STATUS_INVALID_PARAM;
}
if (MMC_CARD_SD(card) && dev->part_curr != PART_ACCESS_DEFAULT) {
DETAIL_PRINTF(SSDK_CRIT,
"sd card only support access default part, part type %d\r\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
else if (MMC_CARD_MMC(card) && type != dev->part_curr) {
mmc_ret = sdrv_mmc_sdhci_switch_part(dev, type);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "mmc switch %d part error\r\n", type);
return mmc_ret;
}
dev->part_curr = type;
}
mmc_ret = sdrv_mmc_sdhci_read(dev, dest, blk_addr, num_blocks);
return mmc_ret;
}
status_t sdrv_mmc_sdhci_write_part(struct mmc_device *dev, void *src,
uint32_t blk_addr,
uint32_t num_blocks, uint32_t type)
{
status_t mmc_ret = SDRV_STATUS_OK;
if ((NULL == dev) || (NULL == src)) {
DETAIL_PRINTF(SSDK_CRIT, "The passed pointer is null\n");
return SDRV_STATUS_INVALID_PARAM;
}
struct mmc_card *card = &dev->card;
uint64_t mmc_size = sdrv_mmc_get_size(dev, type);
if (mmc_size == 0) {
DETAIL_PRINTF(SSDK_CRIT, "mmc part %d not support\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
if (!IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE)) {
DETAIL_PRINTF(SSDK_CRIT, "src address %p not aligned to cache line %d\n", src,
CONFIG_ARCH_CACHE_LINE);
return SDRV_STATUS_INVALID_PARAM;
}
if (num_blocks == 0
|| ((blk_addr + num_blocks) > (mmc_size / card->block_size))) {
DETAIL_PRINTF(SSDK_CRIT,
"num_blocks == 0 or blk_addr + num_blocks out of the mmc size range\n");
DETAIL_PRINTF(SSDK_CRIT,
"blk_addr:%x num_blocks:%x mmc block size:%llx\n", blk_addr, num_blocks,
(mmc_size / card->block_size));
return SDRV_STATUS_INVALID_PARAM;
}
if (MMC_CARD_SD(card) && dev->part_curr != PART_ACCESS_DEFAULT) {
DETAIL_PRINTF(SSDK_CRIT,
"sd card only support access default part, part type %d\r\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
else if (MMC_CARD_MMC(card) && type != dev->part_curr) {
mmc_ret = sdrv_mmc_sdhci_switch_part(dev, type);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "mmc switch %d part error\r\n", type);
return mmc_ret;
}
dev->part_curr = type;
}
mmc_ret = sdrv_mmc_sdhci_write(dev, src, blk_addr, num_blocks);
return mmc_ret;
}
status_t sdrv_mmc_sdhci_erase_part(struct mmc_device *dev,
uint32_t blk_addr,
uint32_t num_blocks, uint32_t type)
{
status_t mmc_ret = SDRV_STATUS_OK;
if (NULL == dev) {
DETAIL_PRINTF(SSDK_CRIT, "The passed pointer is null\n");
return SDRV_STATUS_INVALID_PARAM;
}
struct mmc_card *card = &dev->card;
uint64_t mmc_size = sdrv_mmc_get_size(dev, type);
uint32_t erase_group_size = sdrv_mmc_get_erase_group_size(dev);
if (mmc_size == 0) {
DETAIL_PRINTF(SSDK_CRIT, "mmc part %d not support\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
if (num_blocks == 0
|| ((blk_addr + num_blocks) > (mmc_size / card->block_size))) {
DETAIL_PRINTF(SSDK_CRIT,
"num_blocks == 0 or blk_addr + num_blocks out of the mmc size range\n");
DETAIL_PRINTF(SSDK_CRIT,
"blk_addr:%x num_blocks:%x mmc block size:%llx\n", blk_addr, num_blocks,
(mmc_size / card->block_size));
return SDRV_STATUS_INVALID_PARAM;
}
if (!IS_ALIGNED(blk_addr, (erase_group_size / card->block_size))
|| !IS_ALIGNED(num_blocks, (erase_group_size / card->block_size))) {
DETAIL_PRINTF(SSDK_CRIT,
"blk_addr %x or num_blocks %x not aligned to erase group size %x\n",
blk_addr, num_blocks, erase_group_size / card->block_size);
return SDRV_STATUS_INVALID_PARAM;
}
if (MMC_CARD_SD(card) && dev->part_curr != PART_ACCESS_DEFAULT) {
DETAIL_PRINTF(SSDK_CRIT,
"sd card only support access default part, part type %d\r\n", type);
return SDRV_STATUS_INVALID_PARAM;
}
else if (MMC_CARD_MMC(card) && type != dev->part_curr) {
mmc_ret = sdrv_mmc_sdhci_switch_part(dev, type);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "mmc switch %d part error\r\n", type);
return mmc_ret;
}
dev->part_curr = type;
}
mmc_ret = sdrv_mmc_sdhci_erase(dev, blk_addr,
(uint64_t)num_blocks * MMC_BLK_SZ);
return mmc_ret;
}
static status_t sdrv_sehc_set_clk(struct sdhci_host *host, unsigned int clock)
{
status_t ret = SDRV_STATUS_OK;
uint16_t clk_ctrl = 0;
struct mmc_device *mmc = host->parent;
struct mmc_config_data *mmc_cfg = &mmc->config;
sdrv_ckgen_node_t *clk = mmc_cfg->clk;
/* Disable the clock */
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
if (clock == 0u)
return SDRV_STATUS_OK;
/*
* DDR TIMING need twice base frequency.
*/
if (clk != NULL) {
if ((host->timing == SDHCI_EMMC_HS400_MODE)
|| (host->timing == SDHCI_EMMC_DDR52_MODE)
|| (host->timing == SDHCI_UHS_DDR50_MODE)) {
if (sdrv_ckgen_set_rate(clk, MIN(clock, host->max_clk_rate) * 2))
return SDRV_STATUS_FAIL;
}
else {
if (sdrv_ckgen_set_rate(clk, MIN(clock, host->max_clk_rate)))
return SDRV_STATUS_FAIL;
}
}
DETAIL_PRINTF(SSDK_INFO, "switch card clock to %d\r\n", MIN(clock,
host->max_clk_rate));
clk_ctrl |= SDHCI_INT_CLK_EN;
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
/* Check for clock stable, timeout 150ms */
ret =
sdrv_sdhci_wait_for_bit(host, SDHCI_CLK_CTRL_REG, SDHCI_CLK_STABLE, 0, 150);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error: sdhci clock wait stable timeout!");
return ret;
}
clk_ctrl |= SDHCI_CLK_PLL_EN;
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
/* Check for clock stable, timeout 150ms */
ret =
sdrv_sdhci_wait_for_bit(host, SDHCI_CLK_CTRL_REG, SDHCI_CLK_STABLE, 0, 150);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "Error: sdhci clock wait stable timeout!");
return ret;
}
/* Now clock is stable, enable it */
clk_ctrl = REG_READ16(host, SDHCI_CLK_CTRL_REG);
clk_ctrl |= SDHCI_CLK_EN;
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
host->cur_clk_rate = clock;
return ret;
}
static inline status_t sdrv_sehc_dll_config(struct sdhci_host *host)
{
status_t ret = SDRV_STATUS_OK;
uint16_t clk_ctrl = 0;
uint32_t dll_ctrl = 0;
DETAIL_PRINTF(SSDK_INFO, "sehc dll config!\r\n");
/* Disable the clock */
clk_ctrl = REG_READ16(host, SDHCI_CLK_CTRL_REG);
clk_ctrl &= ~SDHCI_CLK_EN;
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
/* config dll_ctrl */
dll_ctrl = REG_READ32(host, SDHCI_DLL_CTRL);
dll_ctrl |= SDHCI_DLL_EN;
dll_ctrl &= ~(SDHCI_DLL_SLV_TARGET_MASK << SDHCI_DLL_SLV_TARGET_OFFSET);
dll_ctrl |= (0x1f << SDHCI_DLL_SLV_TARGET_OFFSET);
REG_WRITE32(host, dll_ctrl, SDHCI_DLL_CTRL);
/* Enable the clock */
clk_ctrl |= SDHCI_CLK_EN;
REG_WRITE16(host, clk_ctrl, SDHCI_CLK_CTRL_REG);
ret = sdrv_sdhci_wait_for_bit(host, SDHCI_DLL_STATUS, SDHCI_DLL_REF_LOCK, 0,
200);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "sehc dll timeout!\r\n");
return ret;
}
return ret;
}
static void sdrv_sehc_set_uhs_mode(struct sdhci_host *host, uint32_t mode)
{
sdrv_sdhci_set_uhs_mode(host, mode);
#if CONFIG_E3L
/* taishanL DDR50 mode need config DAT_OE_MASK */
if (mode == SDHCI_UHS_DDR50_MODE) {
uint32_t oe_mask;
oe_mask = REG_READ32(host, SDHCI_OE_MASK_SEL_REG);
oe_mask &= ~SDHCI_DAT_OE_MASK_SEL;
oe_mask |= 0x1 << SDHCI_DAT_OE_OFFSET;
REG_WRITE32(host, oe_mask, SDHCI_OE_MASK_SEL_REG);
}
#endif
/* HS400 mode need config dll frequency */
if (mode == SDHCI_EMMC_HS400_MODE) {
if (sdrv_sehc_dll_config(host))
DETAIL_PRINTF(SSDK_CRIT, "sehc dll config failed!\r\n");
}
return;
}
static bool sdrv_sehc_mmc_ending_trans(struct sdhci_host *host)
{
struct mmc_device *dev = host->parent;
struct mmc_config_data *mmc_cfg = &dev->config;
sdrv_scr_t *scr_ctr = mmc_cfg->scr_ctrl;
scr_signal_t *scr_signal = mmc_cfg->scr_signal;
if ((NULL == scr_ctr) || (NULL == scr_signal)) {
DETAIL_PRINTF(SSDK_CRIT,
"sehc ending trans check, not config scr_ctr || scr_signal!\r\n");
return true;
}
return (0x1 == scr_get(scr_ctr, scr_signal));
}
static void sdrv_sehc_hw_reset(struct sdhci_host *host)
{
uint32_t emmc_ctrl;
emmc_ctrl = REG_READ32(host, SDHCI_EMMC_CTRL_REG);
emmc_ctrl &= ~SDHCI_RST_N;
REG_WRITE32(host, emmc_ctrl, SDHCI_EMMC_CTRL_REG);
udelay(5);
emmc_ctrl |= SDHCI_RST_N;
REG_WRITE32(host, emmc_ctrl, SDHCI_EMMC_CTRL_REG);
}
static status_t sdrv_sehc_tuning_sequence(struct sdhci_host *host,
uint32_t opcode,
uint32_t bus_width)
{
status_t ret = SDRV_STATUS_OK;
uint32_t reg;
struct mmc_command cmd;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.cmd_index = opcode;
cmd.argument = 0x0;
cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
cmd.resp_type = SDHCI_CMD_RESP_R1;
cmd.trans_mode = SDHCI_MMC_READ;
cmd.data_present = 0x0;
cmd.data.data_ptr = NULL;
cmd.data.num_blocks = 0x1;
if (cmd.cmd_index == CMD21_SEND_TUNING_BLOCK &&
bus_width == MMC_DATA_BUS_WIDTH_8BIT)
cmd.data.blk_sz = 128;
else
cmd.data.blk_sz = 64;
REG_WRITE16(host, cmd.data.blk_sz, SDHCI_BLKSZ_REG);
if (host->caps.spec_version >= SDHCI_SPEC_VER4_NUM)
REG_WRITE32(host, cmd.data.num_blocks, SDHCI_SDMASA_BLKCNT_REG);
else
REG_WRITE16(host, cmd.data.num_blocks, SDHCI_BLK_CNT_REG);
/* Write to transfer mode register */
REG_WRITE16(host, SDHCI_READ_MODE, SDHCI_TRANS_MODE_REG);
for (int i = 0; i < SEHC_MAX_TUNING_LOOP; i++) {
ret = sdrv_sdhci_send_command(host, &cmd);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "sdhci send hw tuning cmd error!\r\n");
return ret;
}
/* wait for read buf ready, timeout 50ms */
ret = sdrv_sdhci_wait_for_bit(host, SDHCI_NRML_INT_STS_REG,
SDHCI_INT_DATA_AVAIL, 0, 50);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "sdhci tuning timeout!\r\n");
return ret;
}
reg = REG_READ16(host, SDHCI_HOST_CTRL2_REG);
if (!(reg & SDHCI_EXEC_TUNING)) {
/* check SDHCI_SAMPLE_CLK_SEL set,tuning is successful */
if (reg & SDHCI_SAMPLE_CLK_SEL) {
DETAIL_PRINTF(SSDK_INFO, "sdhci: tuning cycle count = %d\r\n", i);
if (i < SDHCI_HW_TUNING_MIN_COUNT)
DETAIL_PRINTF(SSDK_CRIT, "SDHC: tuning cycle abnormal, num is %d!\r\n", i);
return SDRV_STATUS_OK;
}
/* check FAIL_NUM_ > 0,also tuning is successful */
reg = REG_READ32(host, SDHCI_TUNING_STATUS_REG);
if ((reg >> SDHCI_FAIL_NUM_OFFSET) & SDHCI_FAIL_NUM_MASK) {
DETAIL_PRINTF(SSDK_INFO, "sdhci: tuning cycle count = %d\r\n", i);
if (i < 40)
DETAIL_PRINTF(SSDK_CRIT, "SDHC: tuning cycle abnormal, num is %d!\r\n", i);
DETAIL_PRINTF(SSDK_INFO, "sdhci: tuning sw config override\r\n");
uint32_t fail_num = 0;
uint32_t pass_num = 0;
uint32_t override_val = 0;
fail_num = (reg >> SDHCI_FAIL_NUM_OFFSET) & SDHCI_FAIL_NUM_MASK;
pass_num = (reg >> SDHCI_PASS_NUM_OFFSET) & SDHCI_PASS_NUM_MASK;
override_val = (fail_num + pass_num) / 2;
/* set override */
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg |= SDHCI_OVERRIDE_EN;
reg &= ~(SDHCI_OVERRIDE_VAL_MASK << SDHCI_OVERRIDE_VAL_OFFSET);
reg |= override_val << SDHCI_OVERRIDE_VAL_OFFSET;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
/* set SAMPLE_CLK_SEL */
reg = REG_READ16(host, SDHCI_HOST_CTRL2_REG);
reg |= SDHCI_SAMPLE_CLK_SEL;
REG_WRITE16(host, reg, SDHCI_HOST_CTRL2_REG);
return SDRV_STATUS_OK;
}
break;
}
}
return SDRV_STATUS_MMC_TUNING_FAIL;
}
status_t sdrv_sehc_execute_tuning(struct sdhci_host *host, uint32_t opcode,
uint32_t bus_width)
{
status_t ret = SDRV_STATUS_OK;
uint32_t reg;
/* reset the tuning circuit */
sdrv_sdhci_reset_tuning(host);
/* set tuning_clk_stop_en */
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg |= SDHCI_TUNING_CLK_STOP_EN;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
/* start the tuning circuit */
sdrv_sdhci_start_tuning(host);
host->tuning_in_progress = 1;
udelay(1);
/* disable SDHCI_INT_DATA_AVAIL interrupt */
reg = REG_READ16(host, SDHCI_NRML_INT_SIG_EN_REG);
reg &= ~SDHCI_INT_DATA_AVAIL;
REG_WRITE16(host, reg, SDHCI_NRML_INT_SIG_EN_REG);
ret = sdrv_sehc_tuning_sequence(host, opcode, bus_width);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT, "sdhci tuning failed, abort tuning state!\r\n");
sdrv_sdhci_abort_tuning(host);
}
/* clear SDHCI_INT_DATA_AVAIL */
REG_WRITE16(host, SDHCI_INT_DATA_AVAIL, SDHCI_NRML_INT_STS_REG);
/* enable SDHCI_INT_DATA_AVAIL interrupt */
reg = REG_READ16(host, SDHCI_NRML_INT_SIG_EN_REG);
reg |= SDHCI_INT_DATA_AVAIL;
REG_WRITE16(host, reg, SDHCI_NRML_INT_SIG_EN_REG);
/* clear tuning_clk_stop_en */
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg &= ~SDHCI_TUNING_CLK_STOP_EN;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
host->tuning_in_progress = 0;
return ret;
}
static void sdrv_sehc_prepare_tuning(struct sdhci_host *host, uint32_t val)
{
uint32_t reg;
uint16_t clk = 0;
clk = REG_READ16(host, SDHCI_CLK_CTRL_REG);
clk &= ~SDHCI_CLK_EN;
REG_WRITE16(host, clk, SDHCI_CLK_CTRL_REG);
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg |= SDHCI_OVERRIDE_EN;
reg &= ~(SDHCI_OVERRIDE_VAL_MASK << SDHCI_OVERRIDE_VAL_OFFSET);
reg |= val << SDHCI_OVERRIDE_VAL_OFFSET;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
clk = REG_READ16(host, SDHCI_CLK_CTRL_REG);
clk |= SDHCI_CLK_EN;
REG_WRITE16(host, clk, SDHCI_CLK_CTRL_REG);
}
__UNUSED static void sdrv_sehc_post_tuning(struct sdhci_host *host)
{
sdrv_sdhci_reset_tuning(host);
}
status_t sdrv_sehc_sw_execute_tuning(struct sdhci_host *host, uint32_t opcode,
uint32_t bus_width)
{
status_t ret = SDRV_STATUS_OK;
uint32_t min = 0;
uint32_t max = 0;
uint32_t avg = 0;
uint32_t reg = 0;
/* reset the tuning circuit */
sdrv_sdhci_reset_tuning(host);
/* set tuning_clk_stop_en */
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg |= SDHCI_TUNING_CLK_STOP_EN;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
/* config the sample tuning circuit */
uint16_t ctrl2;
ctrl2 = REG_READ16(host, SDHCI_HOST_CTRL2_REG);
ctrl2 |= SDHCI_SAMPLE_CLK_SEL;
REG_WRITE16(host, ctrl2, SDHCI_HOST_CTRL2_REG);
host->sw_tuning_in_progress = 1;
udelay(1);
/* find the mininum delay first which can not pass tuning */
min = SEHC_TUNE_CTRL_MIN;
#if (CONFIG_E3 || CONFIG_D3)
if (IS_P0)
min = SEHC_1_0_TUNE_CTRL_MIN;
#endif
while (min < SEHC_TUNE_CTRL_MAX) {
sdrv_sehc_prepare_tuning(host, min);
if (sdrv_mmc_send_tuning((struct mmc_device *)host->parent, opcode, bus_width))
break;
min += SEHC_TUNE_CTRL_STEP;
}
DETAIL_PRINTF(SSDK_INFO, "sw tuning not pass tuning %d\r\n", min);
/* find the mininum delay first which can pass tuning */
min = min + SEHC_TUNE_CTRL_STEP;
SW_TUNING:
while (min < SEHC_TUNE_CTRL_MAX) {
sdrv_sehc_prepare_tuning(host, min);
if (!sdrv_mmc_send_tuning((struct mmc_device *)host->parent, opcode, bus_width))
break;
min += SEHC_TUNE_CTRL_STEP;
}
DETAIL_PRINTF(SSDK_INFO, "sw tuning pass tuning %d\r\n", min);
/* find the mininum delay first which can not pass tuning */
max = min + SEHC_TUNE_CTRL_STEP;
while (max < SEHC_TUNE_CTRL_MAX) {
sdrv_sehc_prepare_tuning(host, max);
if (sdrv_mmc_send_tuning((struct mmc_device *)host->parent, opcode, bus_width))
break;
max += SEHC_TUNE_CTRL_STEP;
}
DETAIL_PRINTF(SSDK_INFO, "sw tuning not pass tuning %d\r\n", max);
if (min >= SEHC_TUNE_CTRL_MAX) {
ret = SDRV_STATUS_MMC_TUNING_FAIL;
DETAIL_PRINTF(SSDK_CRIT, "sw tuning find pass window error\r\n");
goto out;
}
if ((max - min) > SEHC_TUNE_WINDOW_MIN) {
avg = (min + max) / 2;
DETAIL_PRINTF(SSDK_INFO, "sw tuning window %d ~ %d avg:%d\r\n", min, max, avg);
sdrv_sehc_prepare_tuning(host, avg);
ret = sdrv_mmc_send_tuning((struct mmc_device *)host->parent, opcode,
bus_width);
if (ret) {
DETAIL_PRINTF(SSDK_CRIT,
"sw tuning window %d ~ %d avg:%d, tuning cmd error\r\n",
min, max, avg);
goto out;
}
}
else {
min = max + SEHC_TUNE_CTRL_STEP;
DETAIL_PRINTF(SSDK_INFO, "sw tuning window < 5,goto sw_tuning\r\n");
goto SW_TUNING;
}
out:
/* clear tuning_clk_stop_en */
reg = REG_READ32(host, SDHCI_TUNING_CTRL_REG);
reg &= ~SDHCI_TUNING_CLK_STOP_EN;
REG_WRITE32(host, reg, SDHCI_TUNING_CTRL_REG);
host->sw_tuning_in_progress = 0;
return ret;
}
struct sdhci_ops sdhci_sehc_ops = {
.hw_reset = sdrv_sehc_hw_reset,
.set_clock = sdrv_sehc_set_clk,
.set_uhs_mode = sdrv_sehc_set_uhs_mode,
.mmc_ending_trans = sdrv_sehc_mmc_ending_trans,
.platform_execute_tuning = sdrv_sehc_execute_tuning,
};
status_t sdrv_mmc_platform_init(struct mmc_device *dev, void *priv_data)
{
status_t mmc_ret = SDRV_STATUS_OK;
if (NULL == dev) {
DETAIL_PRINTF(SSDK_CRIT, "\r\n %s: mmc_device parameter is null\r\n", __func__);
return SDRV_STATUS_INVALID_PARAM ;
}
struct sdhci_host *host;
struct mmc_config_data *mmc_cfg = &dev->config;
sdrv_ckgen_node_t *clk = mmc_cfg->clk;
struct mmc_sehc *sehc = priv_data;
host = &dev->host;
host->priv_data = sehc;
host->ops = &sdhci_sehc_ops;
host->parent = dev;
#if (CONFIG_E3 || CONFIG_D3)
if (IS_P0)
host->ops->platform_execute_tuning = sdrv_sehc_sw_execute_tuning;
#endif
if (clk != NULL) {
/* Init the ref clock: 400Khz */
if (sdrv_ckgen_set_rate(clk, 400000)) {
DETAIL_PRINTF(SSDK_CRIT, "mmc set ref clock failed!\r\n");
return SDRV_STATUS_FAIL;
}
}
mmc_ret = sdrv_mmc_sdrv_sdhci_init(dev);
if (mmc_ret) {
DETAIL_PRINTF(SSDK_CRIT, "mmc init failed!\r\n");
return mmc_ret;
}
return mmc_ret;
}