1ad71d45eSYann Gautier /* 2ad71d45eSYann Gautier * Copyright (c) 2018, 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 <arch_helpers.h> 10ad71d45eSYann Gautier #include <assert.h> 11ad71d45eSYann Gautier #include <debug.h> 1215e913d4SYann Gautier #include <delay_timer.h> 13ad71d45eSYann Gautier #include <errno.h> 14ad71d45eSYann Gautier #include <mmc.h> 15ad71d45eSYann Gautier #include <stdbool.h> 16ad71d45eSYann Gautier #include <string.h> 17ad71d45eSYann Gautier #include <utils.h> 18ad71d45eSYann Gautier 19ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES 5 20ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES 100 21ad71d45eSYann Gautier 22ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT 19 23ad71d45eSYann Gautier 24ad71d45eSYann Gautier static const struct mmc_ops *ops; 25ad71d45eSYann Gautier static unsigned int mmc_ocr_value; 26ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd; 2707858dd8SHaojian Zhuang static unsigned char mmc_ext_csd[512] __aligned(16); 28ad71d45eSYann Gautier static unsigned int mmc_flags; 29ad71d45eSYann Gautier static struct mmc_device_info *mmc_dev_info; 30ad71d45eSYann Gautier static unsigned int rca; 31ad71d45eSYann Gautier 32ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = { 33ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80 34ad71d45eSYann Gautier }; 35ad71d45eSYann Gautier 36ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = { 37ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 38ad71d45eSYann Gautier }; 39ad71d45eSYann Gautier 40ad71d45eSYann Gautier static bool is_cmd23_enabled(void) 41ad71d45eSYann Gautier { 42ad71d45eSYann Gautier return ((mmc_flags & MMC_FLAG_CMD23) != 0U); 43ad71d45eSYann Gautier } 44ad71d45eSYann Gautier 45ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg, 46ad71d45eSYann Gautier unsigned int r_type, unsigned int *r_data) 47ad71d45eSYann Gautier { 48ad71d45eSYann Gautier struct mmc_cmd cmd; 49ad71d45eSYann Gautier int ret; 50ad71d45eSYann Gautier 51ad71d45eSYann Gautier zeromem(&cmd, sizeof(struct mmc_cmd)); 52ad71d45eSYann Gautier 53ad71d45eSYann Gautier cmd.cmd_idx = idx; 54ad71d45eSYann Gautier cmd.cmd_arg = arg; 55ad71d45eSYann Gautier cmd.resp_type = r_type; 56ad71d45eSYann Gautier 57ad71d45eSYann Gautier ret = ops->send_cmd(&cmd); 58ad71d45eSYann Gautier 59ad71d45eSYann Gautier if ((ret == 0) && (r_data != NULL)) { 60ad71d45eSYann Gautier int i; 61ad71d45eSYann Gautier 62ad71d45eSYann Gautier for (i = 0; i < 4; i++) { 63ad71d45eSYann Gautier *r_data = cmd.resp_data[i]; 64ad71d45eSYann Gautier r_data++; 65ad71d45eSYann Gautier } 66ad71d45eSYann Gautier } 67ad71d45eSYann Gautier 68ad71d45eSYann Gautier if (ret != 0) { 69ad71d45eSYann Gautier VERBOSE("Send command %u error: %d\n", idx, ret); 70ad71d45eSYann Gautier } 71ad71d45eSYann Gautier 72ad71d45eSYann Gautier return ret; 73ad71d45eSYann Gautier } 74ad71d45eSYann Gautier 75ad71d45eSYann Gautier static int mmc_device_state(void) 76ad71d45eSYann Gautier { 77ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES; 78ad71d45eSYann Gautier unsigned int resp_data[4]; 79ad71d45eSYann Gautier 80ad71d45eSYann Gautier do { 81ad71d45eSYann Gautier int ret; 82ad71d45eSYann Gautier 83ad71d45eSYann Gautier if (retries == 0) { 84ad71d45eSYann Gautier ERROR("CMD13 failed after %d retries\n", 85ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES); 86ad71d45eSYann Gautier return -EIO; 87ad71d45eSYann Gautier } 88ad71d45eSYann Gautier 89ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET, 9097d5db8cSYann Gautier MMC_RESPONSE_R1, &resp_data[0]); 91ad71d45eSYann Gautier if (ret != 0) { 92ad71d45eSYann Gautier return ret; 93ad71d45eSYann Gautier } 94ad71d45eSYann Gautier 95ad71d45eSYann Gautier if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) { 96ad71d45eSYann Gautier return -EIO; 97ad71d45eSYann Gautier } 98ad71d45eSYann Gautier 99ad71d45eSYann Gautier retries--; 100ad71d45eSYann Gautier } while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U); 101ad71d45eSYann Gautier 102ad71d45eSYann Gautier return MMC_GET_STATE(resp_data[0]); 103ad71d45eSYann Gautier } 104ad71d45eSYann Gautier 105ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value) 106ad71d45eSYann Gautier { 107ad71d45eSYann Gautier int ret; 108ad71d45eSYann Gautier 109ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(6), 110ad71d45eSYann Gautier EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) | 111ad71d45eSYann Gautier EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL, 11261752898SBryan O'Donoghue MMC_RESPONSE_R1B, NULL); 113ad71d45eSYann Gautier if (ret != 0) { 114ad71d45eSYann Gautier return ret; 115ad71d45eSYann Gautier } 116ad71d45eSYann Gautier 117ad71d45eSYann Gautier do { 118ad71d45eSYann Gautier ret = mmc_device_state(); 119ad71d45eSYann Gautier if (ret < 0) { 120ad71d45eSYann Gautier return ret; 121ad71d45eSYann Gautier } 122ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG); 123ad71d45eSYann Gautier 124ad71d45eSYann Gautier return 0; 125ad71d45eSYann Gautier } 126ad71d45eSYann Gautier 127ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width) 128ad71d45eSYann Gautier { 129ad71d45eSYann Gautier int ret; 130ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES; 131ad71d45eSYann Gautier unsigned int scr[2] = { 0 }; 132ad71d45eSYann Gautier unsigned int bus_width_arg = 0; 133ad71d45eSYann Gautier 134ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr)); 135ad71d45eSYann Gautier if (ret != 0) { 136ad71d45eSYann Gautier return ret; 137ad71d45eSYann Gautier } 138ad71d45eSYann Gautier 139ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 140ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, 14197d5db8cSYann Gautier MMC_RESPONSE_R5, NULL); 142ad71d45eSYann Gautier if (ret != 0) { 143ad71d45eSYann Gautier return ret; 144ad71d45eSYann Gautier } 145ad71d45eSYann Gautier 146ad71d45eSYann Gautier /* ACMD51: SEND_SCR */ 147ad71d45eSYann Gautier do { 14897d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL); 149ad71d45eSYann Gautier if ((ret != 0) && (retries == 0)) { 150ad71d45eSYann Gautier ERROR("ACMD51 failed after %d retries (ret=%d)\n", 151ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES, ret); 152ad71d45eSYann Gautier return ret; 153ad71d45eSYann Gautier } 154ad71d45eSYann Gautier 155ad71d45eSYann Gautier retries--; 156ad71d45eSYann Gautier } while (ret != 0); 157ad71d45eSYann Gautier 158ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&scr, sizeof(scr)); 159ad71d45eSYann Gautier if (ret != 0) { 160ad71d45eSYann Gautier return ret; 161ad71d45eSYann Gautier } 162ad71d45eSYann Gautier 163ad71d45eSYann Gautier if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) && 164ad71d45eSYann Gautier (bus_width == MMC_BUS_WIDTH_4)) { 165ad71d45eSYann Gautier bus_width_arg = 2; 166ad71d45eSYann Gautier } 167ad71d45eSYann Gautier 168ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 169ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, 17097d5db8cSYann Gautier MMC_RESPONSE_R5, NULL); 171ad71d45eSYann Gautier if (ret != 0) { 172ad71d45eSYann Gautier return ret; 173ad71d45eSYann Gautier } 174ad71d45eSYann Gautier 175ad71d45eSYann Gautier /* ACMD6: SET_BUS_WIDTH */ 17697d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL); 177ad71d45eSYann Gautier if (ret != 0) { 178ad71d45eSYann Gautier return ret; 179ad71d45eSYann Gautier } 180ad71d45eSYann Gautier 181ad71d45eSYann Gautier do { 182ad71d45eSYann Gautier ret = mmc_device_state(); 183ad71d45eSYann Gautier if (ret < 0) { 184ad71d45eSYann Gautier return ret; 185ad71d45eSYann Gautier } 186ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG); 187ad71d45eSYann Gautier 188ad71d45eSYann Gautier return 0; 189ad71d45eSYann Gautier } 190ad71d45eSYann Gautier 191ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width) 192ad71d45eSYann Gautier { 193ad71d45eSYann Gautier int ret; 194ad71d45eSYann Gautier unsigned int width = bus_width; 195ad71d45eSYann Gautier 196ad71d45eSYann Gautier if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) { 197ad71d45eSYann Gautier if (width == MMC_BUS_WIDTH_8) { 198ad71d45eSYann Gautier WARN("Wrong bus config for SD-card, force to 4\n"); 199ad71d45eSYann Gautier width = MMC_BUS_WIDTH_4; 200ad71d45eSYann Gautier } 201ad71d45eSYann Gautier ret = mmc_sd_switch(width); 202ad71d45eSYann Gautier if (ret != 0) { 203ad71d45eSYann Gautier return ret; 204ad71d45eSYann Gautier } 205ad71d45eSYann Gautier } else if (mmc_csd.spec_vers == 4U) { 206ad71d45eSYann Gautier ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, 207ad71d45eSYann Gautier (unsigned int)width); 208ad71d45eSYann Gautier if (ret != 0) { 209ad71d45eSYann Gautier return ret; 210ad71d45eSYann Gautier } 211ad71d45eSYann Gautier } else { 212ad71d45eSYann Gautier VERBOSE("Wrong MMC type or spec version\n"); 213ad71d45eSYann Gautier } 214ad71d45eSYann Gautier 215ad71d45eSYann Gautier return ops->set_ios(clk, width); 216ad71d45eSYann Gautier } 217ad71d45eSYann Gautier 218ad71d45eSYann Gautier static int mmc_fill_device_info(void) 219ad71d45eSYann Gautier { 220ad71d45eSYann Gautier unsigned long long c_size; 221ad71d45eSYann Gautier unsigned int speed_idx; 222ad71d45eSYann Gautier unsigned int nb_blocks; 223ad71d45eSYann Gautier unsigned int freq_unit; 224cadb36cbSAntonio Nino Diaz int ret = 0; 225ad71d45eSYann Gautier struct mmc_csd_sd_v2 *csd_sd_v2; 226ad71d45eSYann Gautier 227ad71d45eSYann Gautier switch (mmc_dev_info->mmc_dev_type) { 228ad71d45eSYann Gautier case MMC_IS_EMMC: 229ad71d45eSYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE; 230ad71d45eSYann Gautier 231ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd, 232ad71d45eSYann Gautier sizeof(mmc_ext_csd)); 233ad71d45eSYann Gautier if (ret != 0) { 234ad71d45eSYann Gautier return ret; 235ad71d45eSYann Gautier } 236ad71d45eSYann Gautier 237ad71d45eSYann Gautier /* MMC CMD8: SEND_EXT_CSD */ 23897d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL); 239ad71d45eSYann Gautier if (ret != 0) { 240ad71d45eSYann Gautier return ret; 241ad71d45eSYann Gautier } 242ad71d45eSYann Gautier 243ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&mmc_ext_csd, 244ad71d45eSYann Gautier sizeof(mmc_ext_csd)); 245ad71d45eSYann Gautier if (ret != 0) { 246ad71d45eSYann Gautier return ret; 247ad71d45eSYann Gautier } 248ad71d45eSYann Gautier 24993768644SHaojian Zhuang do { 25093768644SHaojian Zhuang ret = mmc_device_state(); 25193768644SHaojian Zhuang if (ret < 0) { 25293768644SHaojian Zhuang return ret; 25393768644SHaojian Zhuang } 25493768644SHaojian Zhuang } while (ret != MMC_STATE_TRAN); 25593768644SHaojian Zhuang 256ad71d45eSYann Gautier nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) | 257ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) | 258ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) | 259ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24); 260ad71d45eSYann Gautier 261ad71d45eSYann Gautier mmc_dev_info->device_size = (unsigned long long)nb_blocks * 262ad71d45eSYann Gautier mmc_dev_info->block_size; 263ad71d45eSYann Gautier 264ad71d45eSYann Gautier break; 265ad71d45eSYann Gautier 266ad71d45eSYann Gautier case MMC_IS_SD: 267ad71d45eSYann Gautier /* 268ad71d45eSYann Gautier * Use the same mmc_csd struct, as required fields here 269ad71d45eSYann Gautier * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC. 270ad71d45eSYann Gautier */ 271ad71d45eSYann Gautier mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len); 272ad71d45eSYann Gautier 273ad71d45eSYann Gautier c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) | 274ad71d45eSYann Gautier (unsigned long long)mmc_csd.c_size_low; 275ad71d45eSYann Gautier assert(c_size != 0xFFFU); 276ad71d45eSYann Gautier 277ad71d45eSYann Gautier mmc_dev_info->device_size = (c_size + 1U) * 278ad71d45eSYann Gautier BIT_64(mmc_csd.c_size_mult + 2U) * 279ad71d45eSYann Gautier mmc_dev_info->block_size; 280ad71d45eSYann Gautier 281ad71d45eSYann Gautier break; 282ad71d45eSYann Gautier 283ad71d45eSYann Gautier case MMC_IS_SD_HC: 284ad71d45eSYann Gautier assert(mmc_csd.csd_structure == 1U); 285ad71d45eSYann Gautier 286ad71d45eSYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE; 287ad71d45eSYann Gautier 288ad71d45eSYann Gautier /* Need to use mmc_csd_sd_v2 struct */ 289ad71d45eSYann Gautier csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd; 290ad71d45eSYann Gautier c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) | 291ad71d45eSYann Gautier (unsigned long long)csd_sd_v2->c_size_low; 292ad71d45eSYann Gautier 293ad71d45eSYann Gautier mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT; 294ad71d45eSYann Gautier 295ad71d45eSYann Gautier break; 296ad71d45eSYann Gautier 297ad71d45eSYann Gautier default: 298ad71d45eSYann Gautier ret = -EINVAL; 299ad71d45eSYann Gautier break; 300ad71d45eSYann Gautier } 301ad71d45eSYann Gautier 302ad71d45eSYann Gautier if (ret != 0) { 303ad71d45eSYann Gautier return ret; 304ad71d45eSYann Gautier } 305ad71d45eSYann Gautier 306ad71d45eSYann Gautier speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >> 307ad71d45eSYann Gautier CSD_TRAN_SPEED_MULT_SHIFT; 308ad71d45eSYann Gautier 309ad71d45eSYann Gautier assert(speed_idx > 0U); 310ad71d45eSYann Gautier 311ad71d45eSYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 312ad71d45eSYann Gautier mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx]; 313ad71d45eSYann Gautier } else { 314ad71d45eSYann Gautier mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx]; 315ad71d45eSYann Gautier } 316ad71d45eSYann Gautier 317ad71d45eSYann Gautier freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK; 318ad71d45eSYann Gautier while (freq_unit != 0U) { 319ad71d45eSYann Gautier mmc_dev_info->max_bus_freq *= 10U; 320ad71d45eSYann Gautier --freq_unit; 321ad71d45eSYann Gautier } 322ad71d45eSYann Gautier 323ad71d45eSYann Gautier mmc_dev_info->max_bus_freq *= 10000U; 324ad71d45eSYann Gautier 325ad71d45eSYann Gautier return 0; 326ad71d45eSYann Gautier } 327ad71d45eSYann Gautier 328ad71d45eSYann Gautier static int sd_send_op_cond(void) 329ad71d45eSYann Gautier { 33015e913d4SYann Gautier int n; 331ad71d45eSYann Gautier unsigned int resp_data[4]; 332ad71d45eSYann Gautier 33315e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) { 334ad71d45eSYann Gautier int ret; 335ad71d45eSYann Gautier 336ad71d45eSYann Gautier /* CMD55: Application Specific Command */ 33797d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL); 338ad71d45eSYann Gautier if (ret != 0) { 339ad71d45eSYann Gautier return ret; 340ad71d45eSYann Gautier } 341ad71d45eSYann Gautier 342ad71d45eSYann Gautier /* ACMD41: SD_SEND_OP_COND */ 34394522ff7SBryan O'Donoghue ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R3, 344ad71d45eSYann Gautier &resp_data[0]); 345ad71d45eSYann Gautier if (ret != 0) { 346ad71d45eSYann Gautier return ret; 347ad71d45eSYann Gautier } 348ad71d45eSYann Gautier 34915e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) { 350ad71d45eSYann Gautier mmc_ocr_value = resp_data[0]; 351ad71d45eSYann Gautier 352ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_HCS) != 0U) { 353ad71d45eSYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC; 354ad71d45eSYann Gautier } else { 355ad71d45eSYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD; 356ad71d45eSYann Gautier } 357ad71d45eSYann Gautier 358ad71d45eSYann Gautier return 0; 359ad71d45eSYann Gautier } 360ad71d45eSYann Gautier 36115e913d4SYann Gautier mdelay(1); 36215e913d4SYann Gautier } 36315e913d4SYann Gautier 36415e913d4SYann Gautier ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES); 36515e913d4SYann Gautier 36615e913d4SYann Gautier return -EIO; 36715e913d4SYann Gautier } 36815e913d4SYann Gautier 36915e913d4SYann Gautier static int mmc_reset_to_idle(void) 370ad71d45eSYann Gautier { 371ad71d45eSYann Gautier int ret; 37215e913d4SYann Gautier 373ad71d45eSYann Gautier /* CMD0: reset to IDLE */ 374ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL); 375ad71d45eSYann Gautier if (ret != 0) { 376ad71d45eSYann Gautier return ret; 377ad71d45eSYann Gautier } 378ad71d45eSYann Gautier 37915e913d4SYann Gautier mdelay(2); 38015e913d4SYann Gautier 38115e913d4SYann Gautier return 0; 382ad71d45eSYann Gautier } 383ad71d45eSYann Gautier 38415e913d4SYann Gautier static int mmc_send_op_cond(void) 38515e913d4SYann Gautier { 38615e913d4SYann Gautier int ret, n; 38715e913d4SYann Gautier unsigned int resp_data[4]; 38815e913d4SYann Gautier 38977614a99SYann Gautier ret = mmc_reset_to_idle(); 39077614a99SYann Gautier if (ret != 0) { 39177614a99SYann Gautier return ret; 39277614a99SYann Gautier }; 39315e913d4SYann Gautier 39415e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) { 395ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE | 396ad71d45eSYann Gautier OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7, 39794522ff7SBryan O'Donoghue MMC_RESPONSE_R3, &resp_data[0]); 398ad71d45eSYann Gautier if (ret != 0) { 399ad71d45eSYann Gautier return ret; 400ad71d45eSYann Gautier } 401ad71d45eSYann Gautier 40215e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) { 403ad71d45eSYann Gautier mmc_ocr_value = resp_data[0]; 404ad71d45eSYann Gautier return 0; 405ad71d45eSYann Gautier } 406ad71d45eSYann Gautier 407*7d639429SJoakim Bech mdelay(10); 40815e913d4SYann Gautier } 40915e913d4SYann Gautier 41015e913d4SYann Gautier ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES); 41115e913d4SYann Gautier 41215e913d4SYann Gautier return -EIO; 41315e913d4SYann Gautier } 41415e913d4SYann Gautier 415ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width) 416ad71d45eSYann Gautier { 417ad71d45eSYann Gautier int ret; 418ad71d45eSYann Gautier unsigned int resp_data[4]; 419ad71d45eSYann Gautier 420ad71d45eSYann Gautier ops->init(); 421ad71d45eSYann Gautier 42277614a99SYann Gautier ret = mmc_reset_to_idle(); 42377614a99SYann Gautier if (ret != 0) { 42477614a99SYann Gautier return ret; 42577614a99SYann Gautier }; 426ad71d45eSYann Gautier 427e74dc940SHaojian Zhuang if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 428e74dc940SHaojian Zhuang ret = mmc_send_op_cond(); 429e74dc940SHaojian Zhuang } else { 430ad71d45eSYann Gautier /* CMD8: Send Interface Condition Command */ 431ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN, 43297d5db8cSYann Gautier MMC_RESPONSE_R5, &resp_data[0]); 433ad71d45eSYann Gautier 434ad71d45eSYann Gautier if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) { 435ad71d45eSYann Gautier ret = sd_send_op_cond(); 436e74dc940SHaojian Zhuang } 437ad71d45eSYann Gautier } 438ad71d45eSYann Gautier if (ret != 0) { 439ad71d45eSYann Gautier return ret; 440ad71d45eSYann Gautier } 441ad71d45eSYann Gautier 442ad71d45eSYann Gautier /* CMD2: Card Identification */ 443a2a69bc8SShawn Guo ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL); 444ad71d45eSYann Gautier if (ret != 0) { 445ad71d45eSYann Gautier return ret; 446ad71d45eSYann Gautier } 447ad71d45eSYann Gautier 448ad71d45eSYann Gautier /* CMD3: Set Relative Address */ 449ad71d45eSYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { 450ad71d45eSYann Gautier rca = MMC_FIX_RCA; 451ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET, 45297d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 453ad71d45eSYann Gautier if (ret != 0) { 454ad71d45eSYann Gautier return ret; 455ad71d45eSYann Gautier } 456ad71d45eSYann Gautier } else { 457ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), 0, 45897d5db8cSYann Gautier MMC_RESPONSE_R6, &resp_data[0]); 459ad71d45eSYann Gautier if (ret != 0) { 460ad71d45eSYann Gautier return ret; 461ad71d45eSYann Gautier } 462ad71d45eSYann Gautier 463ad71d45eSYann Gautier rca = (resp_data[0] & 0xFFFF0000U) >> 16; 464ad71d45eSYann Gautier } 465ad71d45eSYann Gautier 466ad71d45eSYann Gautier /* CMD9: CSD Register */ 467ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET, 468a2a69bc8SShawn Guo MMC_RESPONSE_R2, &resp_data[0]); 469ad71d45eSYann Gautier if (ret != 0) { 470ad71d45eSYann Gautier return ret; 471ad71d45eSYann Gautier } 472ad71d45eSYann Gautier 473ad71d45eSYann Gautier memcpy(&mmc_csd, &resp_data, sizeof(resp_data)); 474ad71d45eSYann Gautier 475ad71d45eSYann Gautier /* CMD7: Select Card */ 476ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET, 47797d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 478ad71d45eSYann Gautier if (ret != 0) { 479ad71d45eSYann Gautier return ret; 480ad71d45eSYann Gautier } 481ad71d45eSYann Gautier 482ad71d45eSYann Gautier do { 483ad71d45eSYann Gautier ret = mmc_device_state(); 484ad71d45eSYann Gautier if (ret < 0) { 485ad71d45eSYann Gautier return ret; 486ad71d45eSYann Gautier } 487ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN); 488ad71d45eSYann Gautier 489bd4e3deeSHaojian Zhuang ret = mmc_set_ios(clk, bus_width); 490ad71d45eSYann Gautier if (ret != 0) { 491ad71d45eSYann Gautier return ret; 492ad71d45eSYann Gautier } 493ad71d45eSYann Gautier 494bd4e3deeSHaojian Zhuang return mmc_fill_device_info(); 495ad71d45eSYann Gautier } 496ad71d45eSYann Gautier 497ea315a69SHaojian Zhuang size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size) 498ad71d45eSYann Gautier { 499ad71d45eSYann Gautier int ret; 500ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg; 501ad71d45eSYann Gautier 502ad71d45eSYann Gautier assert((ops != NULL) && 503ad71d45eSYann Gautier (ops->read != NULL) && 504ad71d45eSYann Gautier (size != 0U) && 505ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U)); 506ad71d45eSYann Gautier 507ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size); 508ad71d45eSYann Gautier if (ret != 0) { 509ad71d45eSYann Gautier return 0; 510ad71d45eSYann Gautier } 511ad71d45eSYann Gautier 512ad71d45eSYann Gautier if (is_cmd23_enabled()) { 513ad71d45eSYann Gautier /* Set block count */ 514ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, 51597d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 516ad71d45eSYann Gautier if (ret != 0) { 517ad71d45eSYann Gautier return 0; 518ad71d45eSYann Gautier } 519ad71d45eSYann Gautier 520ad71d45eSYann Gautier cmd_idx = MMC_CMD(18); 521ad71d45eSYann Gautier } else { 522ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) { 523ad71d45eSYann Gautier cmd_idx = MMC_CMD(18); 524ad71d45eSYann Gautier } else { 525ad71d45eSYann Gautier cmd_idx = MMC_CMD(17); 526ad71d45eSYann Gautier } 527ad71d45eSYann Gautier } 528ad71d45eSYann Gautier 529ad71d45eSYann Gautier if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) && 530ad71d45eSYann Gautier (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) { 531ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE; 532ad71d45eSYann Gautier } else { 533ad71d45eSYann Gautier cmd_arg = lba; 534ad71d45eSYann Gautier } 535ad71d45eSYann Gautier 53697d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL); 537ad71d45eSYann Gautier if (ret != 0) { 538ad71d45eSYann Gautier return 0; 539ad71d45eSYann Gautier } 540ad71d45eSYann Gautier 541ad71d45eSYann Gautier ret = ops->read(lba, buf, size); 542ad71d45eSYann Gautier if (ret != 0) { 543ad71d45eSYann Gautier return 0; 544ad71d45eSYann Gautier } 545ad71d45eSYann Gautier 546ad71d45eSYann Gautier /* Wait buffer empty */ 547ad71d45eSYann Gautier do { 548ad71d45eSYann Gautier ret = mmc_device_state(); 549ad71d45eSYann Gautier if (ret < 0) { 550ad71d45eSYann Gautier return 0; 551ad71d45eSYann Gautier } 552ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA)); 553ad71d45eSYann Gautier 554ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { 55561752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL); 556ad71d45eSYann Gautier if (ret != 0) { 557ad71d45eSYann Gautier return 0; 558ad71d45eSYann Gautier } 559ad71d45eSYann Gautier } 560ad71d45eSYann Gautier 561ad71d45eSYann Gautier return size; 562ad71d45eSYann Gautier } 563ad71d45eSYann Gautier 564ea315a69SHaojian Zhuang size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size) 565ad71d45eSYann Gautier { 566ad71d45eSYann Gautier int ret; 567ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg; 568ad71d45eSYann Gautier 569ad71d45eSYann Gautier assert((ops != NULL) && 570ad71d45eSYann Gautier (ops->write != NULL) && 571ad71d45eSYann Gautier (size != 0U) && 572ad71d45eSYann Gautier ((buf & MMC_BLOCK_MASK) == 0U) && 573ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U)); 574ad71d45eSYann Gautier 575ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size); 576ad71d45eSYann Gautier if (ret != 0) { 577ad71d45eSYann Gautier return 0; 578ad71d45eSYann Gautier } 579ad71d45eSYann Gautier 580ad71d45eSYann Gautier if (is_cmd23_enabled()) { 581ad71d45eSYann Gautier /* Set block count */ 582ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, 58397d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 584ad71d45eSYann Gautier if (ret != 0) { 585ad71d45eSYann Gautier return 0; 586ad71d45eSYann Gautier } 587ad71d45eSYann Gautier 588ad71d45eSYann Gautier cmd_idx = MMC_CMD(25); 589ad71d45eSYann Gautier } else { 590ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) { 591ad71d45eSYann Gautier cmd_idx = MMC_CMD(25); 592ad71d45eSYann Gautier } else { 593ad71d45eSYann Gautier cmd_idx = MMC_CMD(24); 594ad71d45eSYann Gautier } 595ad71d45eSYann Gautier } 596ad71d45eSYann Gautier 597ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) { 598ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE; 599ad71d45eSYann Gautier } else { 600ad71d45eSYann Gautier cmd_arg = lba; 601ad71d45eSYann Gautier } 602ad71d45eSYann Gautier 60397d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL); 604ad71d45eSYann Gautier if (ret != 0) { 605ad71d45eSYann Gautier return 0; 606ad71d45eSYann Gautier } 607ad71d45eSYann Gautier 608ad71d45eSYann Gautier ret = ops->write(lba, buf, size); 609ad71d45eSYann Gautier if (ret != 0) { 610ad71d45eSYann Gautier return 0; 611ad71d45eSYann Gautier } 612ad71d45eSYann Gautier 613ad71d45eSYann Gautier /* Wait buffer empty */ 614ad71d45eSYann Gautier do { 615ad71d45eSYann Gautier ret = mmc_device_state(); 616ad71d45eSYann Gautier if (ret < 0) { 617ad71d45eSYann Gautier return 0; 618ad71d45eSYann Gautier } 619ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV)); 620ad71d45eSYann Gautier 621ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { 62261752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL); 623ad71d45eSYann Gautier if (ret != 0) { 624ad71d45eSYann Gautier return 0; 625ad71d45eSYann Gautier } 626ad71d45eSYann Gautier } 627ad71d45eSYann Gautier 628ad71d45eSYann Gautier return size; 629ad71d45eSYann Gautier } 630ad71d45eSYann Gautier 631ea315a69SHaojian Zhuang size_t mmc_erase_blocks(int lba, size_t size) 632ad71d45eSYann Gautier { 633ad71d45eSYann Gautier int ret; 634ad71d45eSYann Gautier 635ad71d45eSYann Gautier assert(ops != NULL); 636ad71d45eSYann Gautier assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U)); 637ad71d45eSYann Gautier 63897d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL); 639ad71d45eSYann Gautier if (ret != 0) { 640ad71d45eSYann Gautier return 0; 641ad71d45eSYann Gautier } 642ad71d45eSYann Gautier 643ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U, 64497d5db8cSYann Gautier MMC_RESPONSE_R1, NULL); 645ad71d45eSYann Gautier if (ret != 0) { 646ad71d45eSYann Gautier return 0; 647ad71d45eSYann Gautier } 648ad71d45eSYann Gautier 64997d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL); 650ad71d45eSYann Gautier if (ret != 0) { 651ad71d45eSYann Gautier return 0; 652ad71d45eSYann Gautier } 653ad71d45eSYann Gautier 654ad71d45eSYann Gautier do { 655ad71d45eSYann Gautier ret = mmc_device_state(); 656ad71d45eSYann Gautier if (ret < 0) { 657ad71d45eSYann Gautier return 0; 658ad71d45eSYann Gautier } 659ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN); 660ad71d45eSYann Gautier 661ad71d45eSYann Gautier return size; 662ad71d45eSYann Gautier } 663ad71d45eSYann Gautier 664ad71d45eSYann Gautier static inline void mmc_rpmb_enable(void) 665ad71d45eSYann Gautier { 666ad71d45eSYann Gautier mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG, 667ad71d45eSYann Gautier PART_CFG_BOOT_PARTITION1_ENABLE | 668ad71d45eSYann Gautier PART_CFG_PARTITION1_ACCESS); 669ad71d45eSYann Gautier } 670ad71d45eSYann Gautier 671ad71d45eSYann Gautier static inline void mmc_rpmb_disable(void) 672ad71d45eSYann Gautier { 673ad71d45eSYann Gautier mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG, 674ad71d45eSYann Gautier PART_CFG_BOOT_PARTITION1_ENABLE); 675ad71d45eSYann Gautier } 676ad71d45eSYann Gautier 677ea315a69SHaojian Zhuang size_t mmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size) 678ad71d45eSYann Gautier { 679ad71d45eSYann Gautier size_t size_read; 680ad71d45eSYann Gautier 681ad71d45eSYann Gautier mmc_rpmb_enable(); 682ad71d45eSYann Gautier size_read = mmc_read_blocks(lba, buf, size); 683ad71d45eSYann Gautier mmc_rpmb_disable(); 684ad71d45eSYann Gautier 685ad71d45eSYann Gautier return size_read; 686ad71d45eSYann Gautier } 687ad71d45eSYann Gautier 688ea315a69SHaojian Zhuang size_t mmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size) 689ad71d45eSYann Gautier { 690ad71d45eSYann Gautier size_t size_written; 691ad71d45eSYann Gautier 692ad71d45eSYann Gautier mmc_rpmb_enable(); 693ad71d45eSYann Gautier size_written = mmc_write_blocks(lba, buf, size); 694ad71d45eSYann Gautier mmc_rpmb_disable(); 695ad71d45eSYann Gautier 696ad71d45eSYann Gautier return size_written; 697ad71d45eSYann Gautier } 698ad71d45eSYann Gautier 699ea315a69SHaojian Zhuang size_t mmc_rpmb_erase_blocks(int lba, size_t size) 700ad71d45eSYann Gautier { 701ad71d45eSYann Gautier size_t size_erased; 702ad71d45eSYann Gautier 703ad71d45eSYann Gautier mmc_rpmb_enable(); 704ad71d45eSYann Gautier size_erased = mmc_erase_blocks(lba, size); 705ad71d45eSYann Gautier mmc_rpmb_disable(); 706ad71d45eSYann Gautier 707ad71d45eSYann Gautier return size_erased; 708ad71d45eSYann Gautier } 709ad71d45eSYann Gautier 710ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, 711ad71d45eSYann Gautier unsigned int width, unsigned int flags, 712ad71d45eSYann Gautier struct mmc_device_info *device_info) 713ad71d45eSYann Gautier { 714ad71d45eSYann Gautier assert((ops_ptr != NULL) && 715ad71d45eSYann Gautier (ops_ptr->init != NULL) && 716ad71d45eSYann Gautier (ops_ptr->send_cmd != NULL) && 717ad71d45eSYann Gautier (ops_ptr->set_ios != NULL) && 718ad71d45eSYann Gautier (ops_ptr->prepare != NULL) && 719ad71d45eSYann Gautier (ops_ptr->read != NULL) && 720ad71d45eSYann Gautier (ops_ptr->write != NULL) && 721ad71d45eSYann Gautier (device_info != NULL) && 722ad71d45eSYann Gautier (clk != 0) && 723ad71d45eSYann Gautier ((width == MMC_BUS_WIDTH_1) || 724ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_4) || 725ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_8) || 726ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_4) || 727ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_8))); 728ad71d45eSYann Gautier 729ad71d45eSYann Gautier ops = ops_ptr; 730ad71d45eSYann Gautier mmc_flags = flags; 731ad71d45eSYann Gautier mmc_dev_info = device_info; 732ad71d45eSYann Gautier 733ad71d45eSYann Gautier return mmc_enumerate(clk, width); 734ad71d45eSYann Gautier } 735