1ad71d45eSYann Gautier /* 2f85041a6SAhmad Fatoum * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. 3ad71d45eSYann Gautier * 4ad71d45eSYann Gautier * SPDX-License-Identifier: BSD-3-Clause 5ad71d45eSYann Gautier */ 6ad71d45eSYann Gautier 7ad71d45eSYann Gautier /* Define a simple and generic interface to access eMMC and SD-card devices. */ 8ad71d45eSYann Gautier 9ad71d45eSYann Gautier #include <assert.h> 10ad71d45eSYann Gautier #include <errno.h> 11ad71d45eSYann Gautier #include <stdbool.h> 12ad71d45eSYann Gautier #include <string.h> 1309d40e0eSAntonio Nino Diaz 1409d40e0eSAntonio Nino Diaz #include <arch_helpers.h> 1509d40e0eSAntonio Nino Diaz #include <common/debug.h> 1609d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h> 1709d40e0eSAntonio Nino Diaz #include <drivers/mmc.h> 1809d40e0eSAntonio Nino Diaz #include <lib/utils.h> 19*ccf8392cSJayanth Dodderi Chidanand #include <plat/common/common_def.h> 20ad71d45eSYann Gautier 21ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES 5 22ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES 100 23ad71d45eSYann Gautier 24ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT 19 25ad71d45eSYann Gautier 26ad71d45eSYann Gautier static const struct mmc_ops *ops; 27ad71d45eSYann Gautier static unsigned int mmc_ocr_value; 28ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd; 29e5b267bbSYann Gautier static struct sd_switch_status sd_switch_func_status; 3007858dd8SHaojian Zhuang static unsigned char mmc_ext_csd[512] __aligned(16); 31ad71d45eSYann Gautier static unsigned int mmc_flags; 3270eb88b7SYann Gautier static struct mmc_device_info *mmc_dev_info; 33ad71d45eSYann Gautier static unsigned int rca; 34a468e756STien Hock, Loh static unsigned int scr[2]__aligned(16) = { 0 }; 35ad71d45eSYann Gautier 36ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = { 37ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80 38ad71d45eSYann Gautier }; 39ad71d45eSYann Gautier 40ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = { 41ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 42ad71d45eSYann Gautier }; 43ad71d45eSYann Gautier 44ad71d45eSYann Gautier static bool is_cmd23_enabled(void) 45ad71d45eSYann Gautier { 46ad71d45eSYann Gautier return ((mmc_flags & MMC_FLAG_CMD23) != 0U); 47ad71d45eSYann Gautier } 48ad71d45eSYann Gautier 49e5b267bbSYann Gautier static bool is_sd_cmd6_enabled(void) 50e5b267bbSYann Gautier { 51e5b267bbSYann Gautier return ((mmc_flags & MMC_FLAG_SD_CMD6) != 0U); 52e5b267bbSYann Gautier } 53e5b267bbSYann Gautier 54ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg, 55ad71d45eSYann Gautier unsigned int r_type, unsigned int *r_data) 56ad71d45eSYann Gautier { 57ad71d45eSYann Gautier struct mmc_cmd cmd; 58ad71d45eSYann Gautier int ret; 59ad71d45eSYann Gautier 60ad71d45eSYann Gautier zeromem(&cmd, sizeof(struct mmc_cmd)); 61ad71d45eSYann Gautier 62ad71d45eSYann Gautier cmd.cmd_idx = idx; 63ad71d45eSYann Gautier cmd.cmd_arg = arg; 64ad71d45eSYann Gautier cmd.resp_type = r_type; 65ad71d45eSYann Gautier 66ad71d45eSYann Gautier ret = ops->send_cmd(&cmd); 67ad71d45eSYann Gautier 68ad71d45eSYann Gautier if ((ret == 0) && (r_data != NULL)) { 69ad71d45eSYann Gautier int i; 70ad71d45eSYann Gautier 71ad71d45eSYann Gautier for (i = 0; i < 4; i++) { 72ad71d45eSYann Gautier *r_data = cmd.resp_data[i]; 73ad71d45eSYann Gautier r_data++; 74ad71d45eSYann Gautier } 75ad71d45eSYann Gautier } 76ad71d45eSYann Gautier 77ad71d45eSYann Gautier if (ret != 0) { 78ad71d45eSYann Gautier VERBOSE("Send command %u error: %d\n", idx, ret); 79ad71d45eSYann Gautier } 80ad71d45eSYann Gautier 81ad71d45eSYann Gautier return ret; 82ad71d45eSYann Gautier } 83ad71d45eSYann Gautier 84ad71d45eSYann Gautier static int mmc_device_state(void) 85ad71d45eSYann Gautier { 86ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES; 87ad71d45eSYann Gautier unsigned int resp_data[4]; 88ad71d45eSYann Gautier 89ad71d45eSYann Gautier do { 90ad71d45eSYann Gautier int ret; 91ad71d45eSYann Gautier 92ad71d45eSYann Gautier if (retries == 0) { 93ad71d45eSYann Gautier ERROR("CMD13 failed after %d retries\n", 94ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES); 95ad71d45eSYann Gautier return -EIO; 96ad71d45eSYann Gautier } 97ad71d45eSYann Gautier 98ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET, 9997d5db8cSYann Gautier MMC_RESPONSE_R1, &resp_data[0]); 100ad71d45eSYann Gautier if (ret != 0) { 101a468e756STien Hock, Loh retries--; 102a468e756STien Hock, Loh continue; 103ad71d45eSYann Gautier } 104ad71d45eSYann Gautier 105ad71d45eSYann Gautier if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) { 106ad71d45eSYann Gautier return -EIO; 107ad71d45eSYann Gautier } 108ad71d45eSYann Gautier 109ad71d45eSYann Gautier retries--; 110ad71d45eSYann Gautier } while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U); 111ad71d45eSYann Gautier 112ad71d45eSYann Gautier return MMC_GET_STATE(resp_data[0]); 113ad71d45eSYann Gautier } 114ad71d45eSYann Gautier 1155014b52dSVyacheslav Yurkov static int mmc_send_part_switch_cmd(unsigned int part_config) 1165014b52dSVyacheslav Yurkov { 1175014b52dSVyacheslav Yurkov int ret; 1185014b52dSVyacheslav Yurkov unsigned int part_time = 0; 1195014b52dSVyacheslav Yurkov 1205014b52dSVyacheslav Yurkov ret = mmc_send_cmd(MMC_CMD(6), 1215014b52dSVyacheslav Yurkov EXTCSD_WRITE_BYTES | 1225014b52dSVyacheslav Yurkov EXTCSD_CMD(CMD_EXTCSD_PARTITION_CONFIG) | 1235014b52dSVyacheslav Yurkov EXTCSD_VALUE(part_config) | 1245014b52dSVyacheslav Yurkov EXTCSD_CMD_SET_NORMAL, 1255014b52dSVyacheslav Yurkov MMC_RESPONSE_R1B, NULL); 1265014b52dSVyacheslav Yurkov if (ret != 0) { 1275014b52dSVyacheslav Yurkov return ret; 1285014b52dSVyacheslav Yurkov } 1295014b52dSVyacheslav Yurkov 1305014b52dSVyacheslav Yurkov /* Partition switch timing is in 10ms units */ 1315014b52dSVyacheslav Yurkov part_time = mmc_ext_csd[CMD_EXTCSD_PART_SWITCH_TIME] * 10; 1325014b52dSVyacheslav Yurkov 1335014b52dSVyacheslav Yurkov mdelay(part_time); 1345014b52dSVyacheslav Yurkov 1355014b52dSVyacheslav Yurkov do { 1365014b52dSVyacheslav Yurkov ret = mmc_device_state(); 1375014b52dSVyacheslav Yurkov if (ret < 0) { 1385014b52dSVyacheslav Yurkov return ret; 1395014b52dSVyacheslav Yurkov } 1405014b52dSVyacheslav Yurkov } while (ret == MMC_STATE_PRG); 1415014b52dSVyacheslav Yurkov 1425014b52dSVyacheslav Yurkov return 0; 1435014b52dSVyacheslav Yurkov } 1445014b52dSVyacheslav Yurkov 145ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value) 146ad71d45eSYann Gautier { 147ad71d45eSYann Gautier int ret; 148ad71d45eSYann Gautier 149ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(6), 150ad71d45eSYann Gautier EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) | 151ad71d45eSYann Gautier EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL, 15261752898SBryan O'Donoghue MMC_RESPONSE_R1B, NULL); 153ad71d45eSYann Gautier if (ret != 0) { 154ad71d45eSYann Gautier return ret; 155ad71d45eSYann Gautier } 156ad71d45eSYann Gautier 157ad71d45eSYann Gautier do { 158ad71d45eSYann Gautier ret = mmc_device_state(); 159ad71d45eSYann Gautier if (ret < 0) { 160ad71d45eSYann Gautier return ret; 161ad71d45eSYann Gautier } 162ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG); 163ad71d45eSYann Gautier 164ad71d45eSYann Gautier return 0; 165ad71d45eSYann Gautier } 166ad71d45eSYann Gautier 167ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width) 168ad71d45eSYann Gautier { 169ad71d45eSYann Gautier int ret; 170ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES; 171ad71d45eSYann Gautier unsigned int bus_width_arg = 0; 172ad71d45eSYann Gautier 173ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr)); 174ad71d45eSYann Gautier if (ret != 0) { 175ad71d45eSYann Gautier return ret; 176ad71d45eSYann Gautier } 177ad71d45eSYann Gautier 178ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 179ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, 18097d5db8cSYann Gautier MMC_RESPONSE_R5, NULL); 181ad71d45eSYann Gautier if (ret != 0) { 182ad71d45eSYann Gautier return ret; 183ad71d45eSYann Gautier } 184ad71d45eSYann Gautier 185ad71d45eSYann Gautier /* ACMD51: SEND_SCR */ 186ad71d45eSYann Gautier do { 18797d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL); 188ad71d45eSYann Gautier if ((ret != 0) && (retries == 0)) { 189ad71d45eSYann Gautier ERROR("ACMD51 failed after %d retries (ret=%d)\n", 190ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES, ret); 191ad71d45eSYann Gautier return ret; 192ad71d45eSYann Gautier } 193ad71d45eSYann Gautier 194ad71d45eSYann Gautier retries--; 195ad71d45eSYann Gautier } while (ret != 0); 196ad71d45eSYann Gautier 197ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&scr, sizeof(scr)); 198ad71d45eSYann Gautier if (ret != 0) { 199ad71d45eSYann Gautier return ret; 200ad71d45eSYann Gautier } 201ad71d45eSYann Gautier 202ad71d45eSYann Gautier if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) && 203ad71d45eSYann Gautier (bus_width == MMC_BUS_WIDTH_4)) { 204ad71d45eSYann Gautier bus_width_arg = 2; 205ad71d45eSYann Gautier } 206ad71d45eSYann Gautier 207ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 208ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, 20997d5db8cSYann Gautier MMC_RESPONSE_R5, NULL); 210ad71d45eSYann Gautier if (ret != 0) { 211ad71d45eSYann Gautier return ret; 212ad71d45eSYann Gautier } 213ad71d45eSYann Gautier 214ad71d45eSYann Gautier /* ACMD6: SET_BUS_WIDTH */ 21597d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL); 216ad71d45eSYann Gautier if (ret != 0) { 217ad71d45eSYann Gautier return ret; 218ad71d45eSYann Gautier } 219ad71d45eSYann Gautier 220ad71d45eSYann Gautier do { 221ad71d45eSYann Gautier ret = mmc_device_state(); 222ad71d45eSYann Gautier if (ret < 0) { 223ad71d45eSYann Gautier return ret; 224ad71d45eSYann Gautier } 225ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG); 226ad71d45eSYann Gautier 227ad71d45eSYann Gautier return 0; 228ad71d45eSYann Gautier } 229ad71d45eSYann Gautier 230ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width) 231ad71d45eSYann Gautier { 232ad71d45eSYann Gautier int ret; 233ad71d45eSYann Gautier unsigned int width = bus_width; 234ad71d45eSYann Gautier 23570eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) { 236ad71d45eSYann Gautier if (width == MMC_BUS_WIDTH_8) { 237ad71d45eSYann Gautier WARN("Wrong bus config for SD-card, force to 4\n"); 238ad71d45eSYann Gautier width = MMC_BUS_WIDTH_4; 239ad71d45eSYann Gautier } 240ad71d45eSYann Gautier ret = mmc_sd_switch(width); 241ad71d45eSYann Gautier if (ret != 0) { 242ad71d45eSYann Gautier return ret; 243ad71d45eSYann Gautier } 244ad71d45eSYann Gautier } else if (mmc_csd.spec_vers == 4U) { 245ad71d45eSYann Gautier ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, 246ad71d45eSYann Gautier (unsigned int)width); 247ad71d45eSYann Gautier if (ret != 0) { 248ad71d45eSYann Gautier return ret; 249ad71d45eSYann Gautier } 250ad71d45eSYann Gautier } else { 251ad71d45eSYann Gautier VERBOSE("Wrong MMC type or spec version\n"); 252ad71d45eSYann Gautier } 253ad71d45eSYann Gautier 254ad71d45eSYann Gautier return ops->set_ios(clk, width); 255ad71d45eSYann Gautier } 256ad71d45eSYann Gautier 257ad71d45eSYann Gautier static int mmc_fill_device_info(void) 258ad71d45eSYann Gautier { 259ad71d45eSYann Gautier unsigned long long c_size; 260ad71d45eSYann Gautier unsigned int speed_idx; 261ad71d45eSYann Gautier unsigned int nb_blocks; 262ad71d45eSYann Gautier unsigned int freq_unit; 263cadb36cbSAntonio Nino Diaz int ret = 0; 264ad71d45eSYann Gautier struct mmc_csd_sd_v2 *csd_sd_v2; 265ad71d45eSYann Gautier 26670eb88b7SYann Gautier switch (mmc_dev_info->mmc_dev_type) { 267ad71d45eSYann Gautier case MMC_IS_EMMC: 26870eb88b7SYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE; 269ad71d45eSYann Gautier 270ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd, 271ad71d45eSYann Gautier sizeof(mmc_ext_csd)); 272ad71d45eSYann Gautier if (ret != 0) { 273ad71d45eSYann Gautier return ret; 274ad71d45eSYann Gautier } 275ad71d45eSYann Gautier 276ad71d45eSYann Gautier /* MMC CMD8: SEND_EXT_CSD */ 27797d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL); 278ad71d45eSYann Gautier if (ret != 0) { 279ad71d45eSYann Gautier return ret; 280ad71d45eSYann Gautier } 281ad71d45eSYann Gautier 282ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&mmc_ext_csd, 283ad71d45eSYann Gautier sizeof(mmc_ext_csd)); 284ad71d45eSYann Gautier if (ret != 0) { 285ad71d45eSYann Gautier return ret; 286ad71d45eSYann Gautier } 287ad71d45eSYann Gautier 28893768644SHaojian Zhuang do { 28993768644SHaojian Zhuang ret = mmc_device_state(); 29093768644SHaojian Zhuang if (ret < 0) { 29193768644SHaojian Zhuang return ret; 29293768644SHaojian Zhuang } 29393768644SHaojian Zhuang } while (ret != MMC_STATE_TRAN); 29493768644SHaojian Zhuang 295ad71d45eSYann Gautier nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) | 296ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) | 297ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) | 298ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24); 299ad71d45eSYann Gautier 30070eb88b7SYann Gautier mmc_dev_info->device_size = (unsigned long long)nb_blocks * 30170eb88b7SYann Gautier mmc_dev_info->block_size; 302ad71d45eSYann Gautier 303ad71d45eSYann Gautier break; 304ad71d45eSYann Gautier 305ad71d45eSYann Gautier case MMC_IS_SD: 306ad71d45eSYann Gautier /* 307ad71d45eSYann Gautier * Use the same mmc_csd struct, as required fields here 308ad71d45eSYann Gautier * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC. 309ad71d45eSYann Gautier */ 31070eb88b7SYann Gautier mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len); 311ad71d45eSYann Gautier 312ad71d45eSYann Gautier c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) | 313ad71d45eSYann Gautier (unsigned long long)mmc_csd.c_size_low; 314ad71d45eSYann Gautier assert(c_size != 0xFFFU); 315ad71d45eSYann Gautier 31670eb88b7SYann Gautier mmc_dev_info->device_size = (c_size + 1U) * 317ad71d45eSYann Gautier BIT_64(mmc_csd.c_size_mult + 2U) * 31870eb88b7SYann Gautier mmc_dev_info->block_size; 319ad71d45eSYann Gautier 320ad71d45eSYann Gautier break; 321ad71d45eSYann Gautier 322ad71d45eSYann Gautier case MMC_IS_SD_HC: 323ad71d45eSYann Gautier assert(mmc_csd.csd_structure == 1U); 324ad71d45eSYann Gautier 32570eb88b7SYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE; 326ad71d45eSYann Gautier 327ad71d45eSYann Gautier /* Need to use mmc_csd_sd_v2 struct */ 328ad71d45eSYann Gautier csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd; 329ad71d45eSYann Gautier c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) | 330ad71d45eSYann Gautier (unsigned long long)csd_sd_v2->c_size_low; 331ad71d45eSYann Gautier 33270eb88b7SYann Gautier mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT; 333ad71d45eSYann Gautier 334ad71d45eSYann Gautier break; 335ad71d45eSYann Gautier 336ad71d45eSYann Gautier default: 337ad71d45eSYann Gautier ret = -EINVAL; 338ad71d45eSYann Gautier break; 339ad71d45eSYann Gautier } 340ad71d45eSYann Gautier 3415f9984efSYann Gautier if (ret < 0) { 342ad71d45eSYann Gautier return ret; 343ad71d45eSYann Gautier } 344ad71d45eSYann Gautier 345ad71d45eSYann Gautier speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >> 346ad71d45eSYann Gautier CSD_TRAN_SPEED_MULT_SHIFT; 347ad71d45eSYann Gautier 348ad71d45eSYann Gautier assert(speed_idx > 0U); 349ad71d45eSYann Gautier 35070eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 35170eb88b7SYann Gautier mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx]; 352ad71d45eSYann Gautier } else { 35370eb88b7SYann Gautier mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx]; 354ad71d45eSYann Gautier } 355ad71d45eSYann Gautier 356ad71d45eSYann Gautier freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK; 357ad71d45eSYann Gautier while (freq_unit != 0U) { 35870eb88b7SYann Gautier mmc_dev_info->max_bus_freq *= 10U; 359ad71d45eSYann Gautier --freq_unit; 360ad71d45eSYann Gautier } 361ad71d45eSYann Gautier 36270eb88b7SYann Gautier mmc_dev_info->max_bus_freq *= 10000U; 363ad71d45eSYann Gautier 364ad71d45eSYann Gautier return 0; 365ad71d45eSYann Gautier } 366ad71d45eSYann Gautier 367e5b267bbSYann Gautier static int sd_switch(unsigned int mode, unsigned char group, 368e5b267bbSYann Gautier unsigned char func) 369e5b267bbSYann Gautier { 370e5b267bbSYann Gautier unsigned int group_shift = (group - 1U) * 4U; 371e5b267bbSYann Gautier unsigned int group_mask = GENMASK(group_shift + 3U, group_shift); 372e5b267bbSYann Gautier unsigned int arg; 373e5b267bbSYann Gautier int ret; 374e5b267bbSYann Gautier 375e5b267bbSYann Gautier ret = ops->prepare(0, (uintptr_t)&sd_switch_func_status, 376e5b267bbSYann Gautier sizeof(sd_switch_func_status)); 377e5b267bbSYann Gautier if (ret != 0) { 378e5b267bbSYann Gautier return ret; 379e5b267bbSYann Gautier } 380e5b267bbSYann Gautier 381e5b267bbSYann Gautier /* MMC CMD6: SWITCH_FUNC */ 382e5b267bbSYann Gautier arg = mode | SD_SWITCH_ALL_GROUPS_MASK; 383e5b267bbSYann Gautier arg &= ~group_mask; 384e5b267bbSYann Gautier arg |= func << group_shift; 385e5b267bbSYann Gautier ret = mmc_send_cmd(MMC_CMD(6), arg, MMC_RESPONSE_R1, NULL); 386e5b267bbSYann Gautier if (ret != 0) { 387e5b267bbSYann Gautier return ret; 388e5b267bbSYann Gautier } 389e5b267bbSYann Gautier 390e5b267bbSYann Gautier return ops->read(0, (uintptr_t)&sd_switch_func_status, 391e5b267bbSYann Gautier sizeof(sd_switch_func_status)); 392e5b267bbSYann Gautier } 393e5b267bbSYann Gautier 394ad71d45eSYann Gautier static int sd_send_op_cond(void) 395ad71d45eSYann Gautier { 39615e913d4SYann Gautier int n; 397ad71d45eSYann Gautier unsigned int resp_data[4]; 398ad71d45eSYann Gautier 39915e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) { 400ad71d45eSYann Gautier int ret; 401ad71d45eSYann Gautier 402ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 40397d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL); 404ad71d45eSYann Gautier if (ret != 0) { 405ad71d45eSYann Gautier return ret; 406ad71d45eSYann Gautier } 407ad71d45eSYann Gautier 408ad71d45eSYann Gautier /* ACMD41: SD_SEND_OP_COND */ 409a468e756STien Hock, Loh ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS | 41070eb88b7SYann Gautier mmc_dev_info->ocr_voltage, MMC_RESPONSE_R3, 411ad71d45eSYann Gautier &resp_data[0]); 412ad71d45eSYann Gautier if (ret != 0) { 413ad71d45eSYann Gautier return ret; 414ad71d45eSYann Gautier } 415ad71d45eSYann Gautier 41615e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) { 417ad71d45eSYann Gautier mmc_ocr_value = resp_data[0]; 418ad71d45eSYann Gautier 419ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_HCS) != 0U) { 42070eb88b7SYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC; 421ad71d45eSYann Gautier } else { 42270eb88b7SYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD; 423ad71d45eSYann Gautier } 424ad71d45eSYann Gautier 425ad71d45eSYann Gautier return 0; 426ad71d45eSYann Gautier } 427ad71d45eSYann Gautier 42857f4b6f8SYann Gautier mdelay(10); 42915e913d4SYann Gautier } 43015e913d4SYann Gautier 43115e913d4SYann Gautier ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES); 43215e913d4SYann Gautier 43315e913d4SYann Gautier return -EIO; 43415e913d4SYann Gautier } 43515e913d4SYann Gautier 43615e913d4SYann Gautier static int mmc_reset_to_idle(void) 437ad71d45eSYann Gautier { 438ad71d45eSYann Gautier int ret; 43915e913d4SYann Gautier 440ad71d45eSYann Gautier /* CMD0: reset to IDLE */ 441ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL); 442ad71d45eSYann Gautier if (ret != 0) { 443ad71d45eSYann Gautier return ret; 444ad71d45eSYann Gautier } 445ad71d45eSYann Gautier 44615e913d4SYann Gautier mdelay(2); 44715e913d4SYann Gautier 44815e913d4SYann Gautier return 0; 449ad71d45eSYann Gautier } 450ad71d45eSYann Gautier 45115e913d4SYann Gautier static int mmc_send_op_cond(void) 45215e913d4SYann Gautier { 45315e913d4SYann Gautier int ret, n; 45415e913d4SYann Gautier unsigned int resp_data[4]; 45515e913d4SYann Gautier 45677614a99SYann Gautier ret = mmc_reset_to_idle(); 45777614a99SYann Gautier if (ret != 0) { 45877614a99SYann Gautier return ret; 4594ecd2417SYann Gautier } 46015e913d4SYann Gautier 46115e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) { 462ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE | 463ad71d45eSYann Gautier OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7, 46494522ff7SBryan O'Donoghue MMC_RESPONSE_R3, &resp_data[0]); 465ad71d45eSYann Gautier if (ret != 0) { 466ad71d45eSYann Gautier return ret; 467ad71d45eSYann Gautier } 468ad71d45eSYann Gautier 46915e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) { 470ad71d45eSYann Gautier mmc_ocr_value = resp_data[0]; 471ad71d45eSYann Gautier return 0; 472ad71d45eSYann Gautier } 473ad71d45eSYann Gautier 4747d639429SJoakim Bech mdelay(10); 47515e913d4SYann Gautier } 47615e913d4SYann Gautier 47715e913d4SYann Gautier ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES); 47815e913d4SYann Gautier 47915e913d4SYann Gautier return -EIO; 48015e913d4SYann Gautier } 48115e913d4SYann Gautier 482ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width) 483ad71d45eSYann Gautier { 484ad71d45eSYann Gautier int ret; 485ad71d45eSYann Gautier unsigned int resp_data[4]; 486ad71d45eSYann Gautier 487ad71d45eSYann Gautier ops->init(); 488ad71d45eSYann Gautier 48977614a99SYann Gautier ret = mmc_reset_to_idle(); 49077614a99SYann Gautier if (ret != 0) { 49177614a99SYann Gautier return ret; 4924ecd2417SYann Gautier } 493ad71d45eSYann Gautier 49470eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 495e74dc940SHaojian Zhuang ret = mmc_send_op_cond(); 496e74dc940SHaojian Zhuang } else { 497ad71d45eSYann Gautier /* CMD8: Send Interface Condition Command */ 498ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN, 49997d5db8cSYann Gautier MMC_RESPONSE_R5, &resp_data[0]); 500ad71d45eSYann Gautier 501ad71d45eSYann Gautier if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) { 502ad71d45eSYann Gautier ret = sd_send_op_cond(); 503e74dc940SHaojian Zhuang } 504ad71d45eSYann Gautier } 505ad71d45eSYann Gautier if (ret != 0) { 506ad71d45eSYann Gautier return ret; 507ad71d45eSYann Gautier } 508ad71d45eSYann Gautier 509ad71d45eSYann Gautier /* CMD2: Card Identification */ 510a2a69bc8SShawn Guo ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL); 511ad71d45eSYann Gautier if (ret != 0) { 512ad71d45eSYann Gautier return ret; 513ad71d45eSYann Gautier } 514ad71d45eSYann Gautier 515ad71d45eSYann Gautier /* CMD3: Set Relative Address */ 51670eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 517ad71d45eSYann Gautier rca = MMC_FIX_RCA; 518ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET, 51997d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 520ad71d45eSYann Gautier if (ret != 0) { 521ad71d45eSYann Gautier return ret; 522ad71d45eSYann Gautier } 523ad71d45eSYann Gautier } else { 524ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), 0, 52597d5db8cSYann Gautier MMC_RESPONSE_R6, &resp_data[0]); 526ad71d45eSYann Gautier if (ret != 0) { 527ad71d45eSYann Gautier return ret; 528ad71d45eSYann Gautier } 529ad71d45eSYann Gautier 530ad71d45eSYann Gautier rca = (resp_data[0] & 0xFFFF0000U) >> 16; 531ad71d45eSYann Gautier } 532ad71d45eSYann Gautier 533ad71d45eSYann Gautier /* CMD9: CSD Register */ 534ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET, 535a2a69bc8SShawn Guo MMC_RESPONSE_R2, &resp_data[0]); 536ad71d45eSYann Gautier if (ret != 0) { 537ad71d45eSYann Gautier return ret; 538ad71d45eSYann Gautier } 539ad71d45eSYann Gautier 540ad71d45eSYann Gautier memcpy(&mmc_csd, &resp_data, sizeof(resp_data)); 541ad71d45eSYann Gautier 542ad71d45eSYann Gautier /* CMD7: Select Card */ 543ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET, 54497d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 545ad71d45eSYann Gautier if (ret != 0) { 546ad71d45eSYann Gautier return ret; 547ad71d45eSYann Gautier } 548ad71d45eSYann Gautier 549ad71d45eSYann Gautier do { 550ad71d45eSYann Gautier ret = mmc_device_state(); 551ad71d45eSYann Gautier if (ret < 0) { 552ad71d45eSYann Gautier return ret; 553ad71d45eSYann Gautier } 554ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN); 555ad71d45eSYann Gautier 556bd4e3deeSHaojian Zhuang ret = mmc_set_ios(clk, bus_width); 557ad71d45eSYann Gautier if (ret != 0) { 558ad71d45eSYann Gautier return ret; 559ad71d45eSYann Gautier } 560ad71d45eSYann Gautier 561e5b267bbSYann Gautier ret = mmc_fill_device_info(); 562e5b267bbSYann Gautier if (ret != 0) { 563e5b267bbSYann Gautier return ret; 564e5b267bbSYann Gautier } 565e5b267bbSYann Gautier 566e5b267bbSYann Gautier if (is_sd_cmd6_enabled() && 567e5b267bbSYann Gautier (mmc_dev_info->mmc_dev_type == MMC_IS_SD_HC)) { 568e5b267bbSYann Gautier /* Try to switch to High Speed Mode */ 569e5b267bbSYann Gautier ret = sd_switch(SD_SWITCH_FUNC_CHECK, 1U, 1U); 570e5b267bbSYann Gautier if (ret != 0) { 571e5b267bbSYann Gautier return ret; 572e5b267bbSYann Gautier } 573e5b267bbSYann Gautier 574e5b267bbSYann Gautier if ((sd_switch_func_status.support_g1 & BIT(9)) == 0U) { 575e5b267bbSYann Gautier /* High speed not supported, keep default speed */ 576e5b267bbSYann Gautier return 0; 577e5b267bbSYann Gautier } 578e5b267bbSYann Gautier 579e5b267bbSYann Gautier ret = sd_switch(SD_SWITCH_FUNC_SWITCH, 1U, 1U); 580e5b267bbSYann Gautier if (ret != 0) { 581e5b267bbSYann Gautier return ret; 582e5b267bbSYann Gautier } 583e5b267bbSYann Gautier 584e5b267bbSYann Gautier if ((sd_switch_func_status.sel_g2_g1 & 0x1U) == 0U) { 585e5b267bbSYann Gautier /* Cannot switch to high speed, keep default speed */ 586e5b267bbSYann Gautier return 0; 587e5b267bbSYann Gautier } 588e5b267bbSYann Gautier 589e5b267bbSYann Gautier mmc_dev_info->max_bus_freq = 50000000U; 590e5b267bbSYann Gautier ret = ops->set_ios(clk, bus_width); 591e5b267bbSYann Gautier } 592e5b267bbSYann Gautier 593e5b267bbSYann Gautier return ret; 594ad71d45eSYann Gautier } 595ad71d45eSYann Gautier 596ea315a69SHaojian Zhuang size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size) 597ad71d45eSYann Gautier { 598ad71d45eSYann Gautier int ret; 599ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg; 600ad71d45eSYann Gautier 601ad71d45eSYann Gautier assert((ops != NULL) && 602ad71d45eSYann Gautier (ops->read != NULL) && 603ad71d45eSYann Gautier (size != 0U) && 604ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U)); 605ad71d45eSYann Gautier 606ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size); 607ad71d45eSYann Gautier if (ret != 0) { 608ad71d45eSYann Gautier return 0; 609ad71d45eSYann Gautier } 610ad71d45eSYann Gautier 611ad71d45eSYann Gautier if (is_cmd23_enabled()) { 612ad71d45eSYann Gautier /* Set block count */ 613ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, 61497d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 615ad71d45eSYann Gautier if (ret != 0) { 616ad71d45eSYann Gautier return 0; 617ad71d45eSYann Gautier } 618ad71d45eSYann Gautier 619ad71d45eSYann Gautier cmd_idx = MMC_CMD(18); 620ad71d45eSYann Gautier } else { 621ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) { 622ad71d45eSYann Gautier cmd_idx = MMC_CMD(18); 623ad71d45eSYann Gautier } else { 624ad71d45eSYann Gautier cmd_idx = MMC_CMD(17); 625ad71d45eSYann Gautier } 626ad71d45eSYann Gautier } 627ad71d45eSYann Gautier 628ad71d45eSYann Gautier if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) && 62970eb88b7SYann Gautier (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) { 630ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE; 631ad71d45eSYann Gautier } else { 632ad71d45eSYann Gautier cmd_arg = lba; 633ad71d45eSYann Gautier } 634ad71d45eSYann Gautier 63597d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL); 636ad71d45eSYann Gautier if (ret != 0) { 637ad71d45eSYann Gautier return 0; 638ad71d45eSYann Gautier } 639ad71d45eSYann Gautier 640ad71d45eSYann Gautier ret = ops->read(lba, buf, size); 641ad71d45eSYann Gautier if (ret != 0) { 642ad71d45eSYann Gautier return 0; 643ad71d45eSYann Gautier } 644ad71d45eSYann Gautier 645ad71d45eSYann Gautier /* Wait buffer empty */ 646ad71d45eSYann Gautier do { 647ad71d45eSYann Gautier ret = mmc_device_state(); 648ad71d45eSYann Gautier if (ret < 0) { 649ad71d45eSYann Gautier return 0; 650ad71d45eSYann Gautier } 651ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA)); 652ad71d45eSYann Gautier 653ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { 65461752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL); 655ad71d45eSYann Gautier if (ret != 0) { 656ad71d45eSYann Gautier return 0; 657ad71d45eSYann Gautier } 658ad71d45eSYann Gautier } 659ad71d45eSYann Gautier 660ad71d45eSYann Gautier return size; 661ad71d45eSYann Gautier } 662ad71d45eSYann Gautier 663ea315a69SHaojian Zhuang size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size) 664ad71d45eSYann Gautier { 665ad71d45eSYann Gautier int ret; 666ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg; 667ad71d45eSYann Gautier 668ad71d45eSYann Gautier assert((ops != NULL) && 669ad71d45eSYann Gautier (ops->write != NULL) && 670ad71d45eSYann Gautier (size != 0U) && 671ad71d45eSYann Gautier ((buf & MMC_BLOCK_MASK) == 0U) && 672ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U)); 673ad71d45eSYann Gautier 674ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size); 675ad71d45eSYann Gautier if (ret != 0) { 676ad71d45eSYann Gautier return 0; 677ad71d45eSYann Gautier } 678ad71d45eSYann Gautier 679ad71d45eSYann Gautier if (is_cmd23_enabled()) { 680ad71d45eSYann Gautier /* Set block count */ 681ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, 68297d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 683ad71d45eSYann Gautier if (ret != 0) { 684ad71d45eSYann Gautier return 0; 685ad71d45eSYann Gautier } 686ad71d45eSYann Gautier 687ad71d45eSYann Gautier cmd_idx = MMC_CMD(25); 688ad71d45eSYann Gautier } else { 689ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) { 690ad71d45eSYann Gautier cmd_idx = MMC_CMD(25); 691ad71d45eSYann Gautier } else { 692ad71d45eSYann Gautier cmd_idx = MMC_CMD(24); 693ad71d45eSYann Gautier } 694ad71d45eSYann Gautier } 695ad71d45eSYann Gautier 696ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) { 697ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE; 698ad71d45eSYann Gautier } else { 699ad71d45eSYann Gautier cmd_arg = lba; 700ad71d45eSYann Gautier } 701ad71d45eSYann Gautier 70297d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL); 703ad71d45eSYann Gautier if (ret != 0) { 704ad71d45eSYann Gautier return 0; 705ad71d45eSYann Gautier } 706ad71d45eSYann Gautier 707ad71d45eSYann Gautier ret = ops->write(lba, buf, size); 708ad71d45eSYann Gautier if (ret != 0) { 709ad71d45eSYann Gautier return 0; 710ad71d45eSYann Gautier } 711ad71d45eSYann Gautier 712ad71d45eSYann Gautier /* Wait buffer empty */ 713ad71d45eSYann Gautier do { 714ad71d45eSYann Gautier ret = mmc_device_state(); 715ad71d45eSYann Gautier if (ret < 0) { 716ad71d45eSYann Gautier return 0; 717ad71d45eSYann Gautier } 718ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV)); 719ad71d45eSYann Gautier 720ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { 72161752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL); 722ad71d45eSYann Gautier if (ret != 0) { 723ad71d45eSYann Gautier return 0; 724ad71d45eSYann Gautier } 725ad71d45eSYann Gautier } 726ad71d45eSYann Gautier 727ad71d45eSYann Gautier return size; 728ad71d45eSYann Gautier } 729ad71d45eSYann Gautier 730ea315a69SHaojian Zhuang size_t mmc_erase_blocks(int lba, size_t size) 731ad71d45eSYann Gautier { 732ad71d45eSYann Gautier int ret; 733ad71d45eSYann Gautier 734ad71d45eSYann Gautier assert(ops != NULL); 735ad71d45eSYann Gautier assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U)); 736ad71d45eSYann Gautier 73797d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL); 738ad71d45eSYann Gautier if (ret != 0) { 739ad71d45eSYann Gautier return 0; 740ad71d45eSYann Gautier } 741ad71d45eSYann Gautier 742ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U, 74397d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 744ad71d45eSYann Gautier if (ret != 0) { 745ad71d45eSYann Gautier return 0; 746ad71d45eSYann Gautier } 747ad71d45eSYann Gautier 74897d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL); 749ad71d45eSYann Gautier if (ret != 0) { 750ad71d45eSYann Gautier return 0; 751ad71d45eSYann Gautier } 752ad71d45eSYann Gautier 753ad71d45eSYann Gautier do { 754ad71d45eSYann Gautier ret = mmc_device_state(); 755ad71d45eSYann Gautier if (ret < 0) { 756ad71d45eSYann Gautier return 0; 757ad71d45eSYann Gautier } 758ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN); 759ad71d45eSYann Gautier 760ad71d45eSYann Gautier return size; 761ad71d45eSYann Gautier } 762ad71d45eSYann Gautier 7635014b52dSVyacheslav Yurkov static int mmc_part_switch(unsigned int part_type) 7645014b52dSVyacheslav Yurkov { 7655014b52dSVyacheslav Yurkov uint8_t part_config = mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]; 7665014b52dSVyacheslav Yurkov 7675014b52dSVyacheslav Yurkov part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; 7685014b52dSVyacheslav Yurkov part_config |= part_type; 7695014b52dSVyacheslav Yurkov 7705014b52dSVyacheslav Yurkov return mmc_send_part_switch_cmd(part_config); 7715014b52dSVyacheslav Yurkov } 7725014b52dSVyacheslav Yurkov 7735014b52dSVyacheslav Yurkov static unsigned char mmc_current_boot_part(void) 7745014b52dSVyacheslav Yurkov { 7755014b52dSVyacheslav Yurkov return PART_CFG_CURRENT_BOOT_PARTITION(mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]); 7765014b52dSVyacheslav Yurkov } 7775014b52dSVyacheslav Yurkov 778f85041a6SAhmad Fatoum int mmc_part_switch_current_boot(void) 7795014b52dSVyacheslav Yurkov { 7805014b52dSVyacheslav Yurkov unsigned char current_boot_part = mmc_current_boot_part(); 781f85041a6SAhmad Fatoum int ret; 7825014b52dSVyacheslav Yurkov 7835014b52dSVyacheslav Yurkov if (current_boot_part != 1U && 7845014b52dSVyacheslav Yurkov current_boot_part != 2U) { 7855014b52dSVyacheslav Yurkov ERROR("Got unexpected value for active boot partition, %u\n", current_boot_part); 786f85041a6SAhmad Fatoum return -EIO; 7875014b52dSVyacheslav Yurkov } 7885014b52dSVyacheslav Yurkov 7895014b52dSVyacheslav Yurkov ret = mmc_part_switch(current_boot_part); 7905014b52dSVyacheslav Yurkov if (ret < 0) { 7915014b52dSVyacheslav Yurkov ERROR("Failed to switch to boot partition, %d\n", ret); 792f85041a6SAhmad Fatoum } 793f85041a6SAhmad Fatoum 794f85041a6SAhmad Fatoum return ret; 795f85041a6SAhmad Fatoum } 796f85041a6SAhmad Fatoum 797f85041a6SAhmad Fatoum int mmc_part_switch_user(void) 798f85041a6SAhmad Fatoum { 799f85041a6SAhmad Fatoum int ret; 800f85041a6SAhmad Fatoum 80101c5dd5eSAhmad Fatoum ret = mmc_part_switch(PART_CFG_BOOT_PARTITION_NO_ACCESS); 802f85041a6SAhmad Fatoum if (ret < 0) { 803f85041a6SAhmad Fatoum ERROR("Failed to switch to user partition, %d\n", ret); 804f85041a6SAhmad Fatoum } 805f85041a6SAhmad Fatoum 806f85041a6SAhmad Fatoum return ret; 807f85041a6SAhmad Fatoum } 808f85041a6SAhmad Fatoum 809f462c124SYann Gautier size_t mmc_boot_part_size(void) 810f462c124SYann Gautier { 811f462c124SYann Gautier return mmc_ext_csd[CMD_EXTCSD_BOOT_SIZE_MULT] * SZ_128K; 812f462c124SYann Gautier } 813f462c124SYann Gautier 814f85041a6SAhmad Fatoum size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size) 815f85041a6SAhmad Fatoum { 816f85041a6SAhmad Fatoum size_t size_read; 817f85041a6SAhmad Fatoum int ret; 818f85041a6SAhmad Fatoum 819f85041a6SAhmad Fatoum ret = mmc_part_switch_current_boot(); 820f85041a6SAhmad Fatoum if (ret < 0) { 8215014b52dSVyacheslav Yurkov return 0; 8225014b52dSVyacheslav Yurkov } 8235014b52dSVyacheslav Yurkov 8245014b52dSVyacheslav Yurkov size_read = mmc_read_blocks(lba, buf, size); 8255014b52dSVyacheslav Yurkov 826f85041a6SAhmad Fatoum ret = mmc_part_switch_user(); 8275014b52dSVyacheslav Yurkov if (ret < 0) { 8285014b52dSVyacheslav Yurkov return 0; 8295014b52dSVyacheslav Yurkov } 8305014b52dSVyacheslav Yurkov 8315014b52dSVyacheslav Yurkov return size_read; 8325014b52dSVyacheslav Yurkov } 8335014b52dSVyacheslav Yurkov 834ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, 835ad71d45eSYann Gautier unsigned int width, unsigned int flags, 836ad71d45eSYann Gautier struct mmc_device_info *device_info) 837ad71d45eSYann Gautier { 838ad71d45eSYann Gautier assert((ops_ptr != NULL) && 839ad71d45eSYann Gautier (ops_ptr->init != NULL) && 840ad71d45eSYann Gautier (ops_ptr->send_cmd != NULL) && 841ad71d45eSYann Gautier (ops_ptr->set_ios != NULL) && 842ad71d45eSYann Gautier (ops_ptr->prepare != NULL) && 843ad71d45eSYann Gautier (ops_ptr->read != NULL) && 844ad71d45eSYann Gautier (ops_ptr->write != NULL) && 845ad71d45eSYann Gautier (device_info != NULL) && 846ad71d45eSYann Gautier (clk != 0) && 847ad71d45eSYann Gautier ((width == MMC_BUS_WIDTH_1) || 848ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_4) || 849ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_8) || 850ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_4) || 851ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_8))); 852ad71d45eSYann Gautier 853ad71d45eSYann Gautier ops = ops_ptr; 854ad71d45eSYann Gautier mmc_flags = flags; 85570eb88b7SYann Gautier mmc_dev_info = device_info; 856ad71d45eSYann Gautier 857ad71d45eSYann Gautier return mmc_enumerate(clk, width); 858ad71d45eSYann Gautier } 859