mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 01:24:27 +00:00

Enforce full include path for includes. Deprecate old paths. The following folders inside include/lib have been left unchanged: - include/lib/cpus/${ARCH} - include/lib/el3_runtime/${ARCH} The reason for this change is that having a global namespace for includes isn't a good idea. It defeats one of the advantages of having folders and it introduces problems that are sometimes subtle (because you may not know the header you are actually including if there are two of them). For example, this patch had to be created because two headers were called the same way:e0ea0928d5
("Fix gpio includes of mt8173 platform to avoid collision."). More recently, this patch has had similar problems:46f9b2c3a2
("drivers: add tzc380 support"). This problem was introduced in commit4ecca33988
("Move include and source files to logical locations"). At that time, there weren't too many headers so it wasn't a real issue. However, time has shown that this creates problems. Platforms that want to preserve the way they include headers may add the removed paths to PLAT_INCLUDES, but this is discouraged. Change-Id: I39dc53ed98f9e297a5966e723d1936d6ccf2fc8f Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
735 lines
15 KiB
C
735 lines
15 KiB
C
/*
|
|
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
/* Define a simple and generic interface to access eMMC and SD-card devices. */
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include <arch_helpers.h>
|
|
#include <common/debug.h>
|
|
#include <drivers/delay_timer.h>
|
|
#include <drivers/mmc.h>
|
|
#include <lib/utils.h>
|
|
|
|
#define MMC_DEFAULT_MAX_RETRIES 5
|
|
#define SEND_OP_COND_MAX_RETRIES 100
|
|
|
|
#define MULT_BY_512K_SHIFT 19
|
|
|
|
static const struct mmc_ops *ops;
|
|
static unsigned int mmc_ocr_value;
|
|
static struct mmc_csd_emmc mmc_csd;
|
|
static unsigned char mmc_ext_csd[512] __aligned(16);
|
|
static unsigned int mmc_flags;
|
|
static struct mmc_device_info *mmc_dev_info;
|
|
static unsigned int rca;
|
|
|
|
static const unsigned char tran_speed_base[16] = {
|
|
0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
|
|
};
|
|
|
|
static const unsigned char sd_tran_speed_base[16] = {
|
|
0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
|
|
};
|
|
|
|
static bool is_cmd23_enabled(void)
|
|
{
|
|
return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
|
|
}
|
|
|
|
static int mmc_send_cmd(unsigned int idx, unsigned int arg,
|
|
unsigned int r_type, unsigned int *r_data)
|
|
{
|
|
struct mmc_cmd cmd;
|
|
int ret;
|
|
|
|
zeromem(&cmd, sizeof(struct mmc_cmd));
|
|
|
|
cmd.cmd_idx = idx;
|
|
cmd.cmd_arg = arg;
|
|
cmd.resp_type = r_type;
|
|
|
|
ret = ops->send_cmd(&cmd);
|
|
|
|
if ((ret == 0) && (r_data != NULL)) {
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
*r_data = cmd.resp_data[i];
|
|
r_data++;
|
|
}
|
|
}
|
|
|
|
if (ret != 0) {
|
|
VERBOSE("Send command %u error: %d\n", idx, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mmc_device_state(void)
|
|
{
|
|
int retries = MMC_DEFAULT_MAX_RETRIES;
|
|
unsigned int resp_data[4];
|
|
|
|
do {
|
|
int ret;
|
|
|
|
if (retries == 0) {
|
|
ERROR("CMD13 failed after %d retries\n",
|
|
MMC_DEFAULT_MAX_RETRIES);
|
|
return -EIO;
|
|
}
|
|
|
|
ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R1, &resp_data[0]);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
|
|
return -EIO;
|
|
}
|
|
|
|
retries--;
|
|
} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
|
|
|
|
return MMC_GET_STATE(resp_data[0]);
|
|
}
|
|
|
|
static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
|
|
{
|
|
int ret;
|
|
|
|
ret = mmc_send_cmd(MMC_CMD(6),
|
|
EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
|
|
EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
|
|
MMC_RESPONSE_R1B, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
} while (ret == MMC_STATE_PRG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmc_sd_switch(unsigned int bus_width)
|
|
{
|
|
int ret;
|
|
int retries = MMC_DEFAULT_MAX_RETRIES;
|
|
unsigned int scr[2] = { 0 };
|
|
unsigned int bus_width_arg = 0;
|
|
|
|
ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* CMD55: Application Specific Command */
|
|
ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R5, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* ACMD51: SEND_SCR */
|
|
do {
|
|
ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL);
|
|
if ((ret != 0) && (retries == 0)) {
|
|
ERROR("ACMD51 failed after %d retries (ret=%d)\n",
|
|
MMC_DEFAULT_MAX_RETRIES, ret);
|
|
return ret;
|
|
}
|
|
|
|
retries--;
|
|
} while (ret != 0);
|
|
|
|
ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
|
|
(bus_width == MMC_BUS_WIDTH_4)) {
|
|
bus_width_arg = 2;
|
|
}
|
|
|
|
/* CMD55: Application Specific Command */
|
|
ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R5, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* ACMD6: SET_BUS_WIDTH */
|
|
ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
} while (ret == MMC_STATE_PRG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
|
|
{
|
|
int ret;
|
|
unsigned int width = bus_width;
|
|
|
|
if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
|
|
if (width == MMC_BUS_WIDTH_8) {
|
|
WARN("Wrong bus config for SD-card, force to 4\n");
|
|
width = MMC_BUS_WIDTH_4;
|
|
}
|
|
ret = mmc_sd_switch(width);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
} else if (mmc_csd.spec_vers == 4U) {
|
|
ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
|
|
(unsigned int)width);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
} else {
|
|
VERBOSE("Wrong MMC type or spec version\n");
|
|
}
|
|
|
|
return ops->set_ios(clk, width);
|
|
}
|
|
|
|
static int mmc_fill_device_info(void)
|
|
{
|
|
unsigned long long c_size;
|
|
unsigned int speed_idx;
|
|
unsigned int nb_blocks;
|
|
unsigned int freq_unit;
|
|
int ret = 0;
|
|
struct mmc_csd_sd_v2 *csd_sd_v2;
|
|
|
|
switch (mmc_dev_info->mmc_dev_type) {
|
|
case MMC_IS_EMMC:
|
|
mmc_dev_info->block_size = MMC_BLOCK_SIZE;
|
|
|
|
ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
|
|
sizeof(mmc_ext_csd));
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* MMC CMD8: SEND_EXT_CSD */
|
|
ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
|
|
sizeof(mmc_ext_csd));
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
} while (ret != MMC_STATE_TRAN);
|
|
|
|
nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
|
|
(mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
|
|
(mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
|
|
(mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
|
|
|
|
mmc_dev_info->device_size = (unsigned long long)nb_blocks *
|
|
mmc_dev_info->block_size;
|
|
|
|
break;
|
|
|
|
case MMC_IS_SD:
|
|
/*
|
|
* Use the same mmc_csd struct, as required fields here
|
|
* (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
|
|
*/
|
|
mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
|
|
|
|
c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
|
|
(unsigned long long)mmc_csd.c_size_low;
|
|
assert(c_size != 0xFFFU);
|
|
|
|
mmc_dev_info->device_size = (c_size + 1U) *
|
|
BIT_64(mmc_csd.c_size_mult + 2U) *
|
|
mmc_dev_info->block_size;
|
|
|
|
break;
|
|
|
|
case MMC_IS_SD_HC:
|
|
assert(mmc_csd.csd_structure == 1U);
|
|
|
|
mmc_dev_info->block_size = MMC_BLOCK_SIZE;
|
|
|
|
/* Need to use mmc_csd_sd_v2 struct */
|
|
csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
|
|
c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
|
|
(unsigned long long)csd_sd_v2->c_size_low;
|
|
|
|
mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
|
|
|
|
break;
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
|
|
CSD_TRAN_SPEED_MULT_SHIFT;
|
|
|
|
assert(speed_idx > 0U);
|
|
|
|
if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
|
|
mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
|
|
} else {
|
|
mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
|
|
}
|
|
|
|
freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
|
|
while (freq_unit != 0U) {
|
|
mmc_dev_info->max_bus_freq *= 10U;
|
|
--freq_unit;
|
|
}
|
|
|
|
mmc_dev_info->max_bus_freq *= 10000U;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sd_send_op_cond(void)
|
|
{
|
|
int n;
|
|
unsigned int resp_data[4];
|
|
|
|
for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
|
|
int ret;
|
|
|
|
/* CMD55: Application Specific Command */
|
|
ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* ACMD41: SD_SEND_OP_COND */
|
|
ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R3,
|
|
&resp_data[0]);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if ((resp_data[0] & OCR_POWERUP) != 0U) {
|
|
mmc_ocr_value = resp_data[0];
|
|
|
|
if ((mmc_ocr_value & OCR_HCS) != 0U) {
|
|
mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
|
|
} else {
|
|
mmc_dev_info->mmc_dev_type = MMC_IS_SD;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
mdelay(1);
|
|
}
|
|
|
|
ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int mmc_reset_to_idle(void)
|
|
{
|
|
int ret;
|
|
|
|
/* CMD0: reset to IDLE */
|
|
ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
mdelay(2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmc_send_op_cond(void)
|
|
{
|
|
int ret, n;
|
|
unsigned int resp_data[4];
|
|
|
|
ret = mmc_reset_to_idle();
|
|
if (ret != 0) {
|
|
return ret;
|
|
};
|
|
|
|
for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
|
|
ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
|
|
OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
|
|
MMC_RESPONSE_R3, &resp_data[0]);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if ((resp_data[0] & OCR_POWERUP) != 0U) {
|
|
mmc_ocr_value = resp_data[0];
|
|
return 0;
|
|
}
|
|
|
|
mdelay(1);
|
|
}
|
|
|
|
ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
|
|
{
|
|
int ret;
|
|
unsigned int resp_data[4];
|
|
|
|
ops->init();
|
|
|
|
ret = mmc_reset_to_idle();
|
|
if (ret != 0) {
|
|
return ret;
|
|
};
|
|
|
|
if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
|
|
ret = mmc_send_op_cond();
|
|
} else {
|
|
/* CMD8: Send Interface Condition Command */
|
|
ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
|
|
MMC_RESPONSE_R5, &resp_data[0]);
|
|
|
|
if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
|
|
ret = sd_send_op_cond();
|
|
}
|
|
}
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* CMD2: Card Identification */
|
|
ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* CMD3: Set Relative Address */
|
|
if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
|
|
rca = MMC_FIX_RCA;
|
|
ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
} else {
|
|
ret = mmc_send_cmd(MMC_CMD(3), 0,
|
|
MMC_RESPONSE_R6, &resp_data[0]);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
rca = (resp_data[0] & 0xFFFF0000U) >> 16;
|
|
}
|
|
|
|
/* CMD9: CSD Register */
|
|
ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R2, &resp_data[0]);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
|
|
|
|
/* CMD7: Select Card */
|
|
ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
|
|
MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
} while (ret != MMC_STATE_TRAN);
|
|
|
|
ret = mmc_set_ios(clk, bus_width);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
return mmc_fill_device_info();
|
|
}
|
|
|
|
size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size)
|
|
{
|
|
int ret;
|
|
unsigned int cmd_idx, cmd_arg;
|
|
|
|
assert((ops != NULL) &&
|
|
(ops->read != NULL) &&
|
|
(size != 0U) &&
|
|
((size & MMC_BLOCK_MASK) == 0U));
|
|
|
|
ret = ops->prepare(lba, buf, size);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (is_cmd23_enabled()) {
|
|
/* Set block count */
|
|
ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
|
|
MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
cmd_idx = MMC_CMD(18);
|
|
} else {
|
|
if (size > MMC_BLOCK_SIZE) {
|
|
cmd_idx = MMC_CMD(18);
|
|
} else {
|
|
cmd_idx = MMC_CMD(17);
|
|
}
|
|
}
|
|
|
|
if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
|
|
(mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
|
|
cmd_arg = lba * MMC_BLOCK_SIZE;
|
|
} else {
|
|
cmd_arg = lba;
|
|
}
|
|
|
|
ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = ops->read(lba, buf, size);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Wait buffer empty */
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return 0;
|
|
}
|
|
} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
|
|
|
|
if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
|
|
ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size)
|
|
{
|
|
int ret;
|
|
unsigned int cmd_idx, cmd_arg;
|
|
|
|
assert((ops != NULL) &&
|
|
(ops->write != NULL) &&
|
|
(size != 0U) &&
|
|
((buf & MMC_BLOCK_MASK) == 0U) &&
|
|
((size & MMC_BLOCK_MASK) == 0U));
|
|
|
|
ret = ops->prepare(lba, buf, size);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (is_cmd23_enabled()) {
|
|
/* Set block count */
|
|
ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
|
|
MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
cmd_idx = MMC_CMD(25);
|
|
} else {
|
|
if (size > MMC_BLOCK_SIZE) {
|
|
cmd_idx = MMC_CMD(25);
|
|
} else {
|
|
cmd_idx = MMC_CMD(24);
|
|
}
|
|
}
|
|
|
|
if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
|
|
cmd_arg = lba * MMC_BLOCK_SIZE;
|
|
} else {
|
|
cmd_arg = lba;
|
|
}
|
|
|
|
ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = ops->write(lba, buf, size);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Wait buffer empty */
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return 0;
|
|
}
|
|
} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
|
|
|
|
if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
|
|
ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
size_t mmc_erase_blocks(int lba, size_t size)
|
|
{
|
|
int ret;
|
|
|
|
assert(ops != NULL);
|
|
assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
|
|
|
|
ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
|
|
MMC_RESPONSE_R1, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL);
|
|
if (ret != 0) {
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
ret = mmc_device_state();
|
|
if (ret < 0) {
|
|
return 0;
|
|
}
|
|
} while (ret != MMC_STATE_TRAN);
|
|
|
|
return size;
|
|
}
|
|
|
|
static inline void mmc_rpmb_enable(void)
|
|
{
|
|
mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
|
|
PART_CFG_BOOT_PARTITION1_ENABLE |
|
|
PART_CFG_PARTITION1_ACCESS);
|
|
}
|
|
|
|
static inline void mmc_rpmb_disable(void)
|
|
{
|
|
mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
|
|
PART_CFG_BOOT_PARTITION1_ENABLE);
|
|
}
|
|
|
|
size_t mmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
|
|
{
|
|
size_t size_read;
|
|
|
|
mmc_rpmb_enable();
|
|
size_read = mmc_read_blocks(lba, buf, size);
|
|
mmc_rpmb_disable();
|
|
|
|
return size_read;
|
|
}
|
|
|
|
size_t mmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
|
|
{
|
|
size_t size_written;
|
|
|
|
mmc_rpmb_enable();
|
|
size_written = mmc_write_blocks(lba, buf, size);
|
|
mmc_rpmb_disable();
|
|
|
|
return size_written;
|
|
}
|
|
|
|
size_t mmc_rpmb_erase_blocks(int lba, size_t size)
|
|
{
|
|
size_t size_erased;
|
|
|
|
mmc_rpmb_enable();
|
|
size_erased = mmc_erase_blocks(lba, size);
|
|
mmc_rpmb_disable();
|
|
|
|
return size_erased;
|
|
}
|
|
|
|
int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
|
|
unsigned int width, unsigned int flags,
|
|
struct mmc_device_info *device_info)
|
|
{
|
|
assert((ops_ptr != NULL) &&
|
|
(ops_ptr->init != NULL) &&
|
|
(ops_ptr->send_cmd != NULL) &&
|
|
(ops_ptr->set_ios != NULL) &&
|
|
(ops_ptr->prepare != NULL) &&
|
|
(ops_ptr->read != NULL) &&
|
|
(ops_ptr->write != NULL) &&
|
|
(device_info != NULL) &&
|
|
(clk != 0) &&
|
|
((width == MMC_BUS_WIDTH_1) ||
|
|
(width == MMC_BUS_WIDTH_4) ||
|
|
(width == MMC_BUS_WIDTH_8) ||
|
|
(width == MMC_BUS_WIDTH_DDR_4) ||
|
|
(width == MMC_BUS_WIDTH_DDR_8)));
|
|
|
|
ops = ops_ptr;
|
|
mmc_flags = flags;
|
|
mmc_dev_info = device_info;
|
|
|
|
return mmc_enumerate(clk, width);
|
|
}
|