Skip to content
Snippets Groups Projects
mmc.c 39 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		if (es && et) {
    			eo = (ssr[3] >> 16) & 0x3;
    			mmc->ssr.erase_timeout = (et * 1000) / es;
    			mmc->ssr.erase_offset = eo * 1000;
    		}
    	} else {
    		debug("Invalid Allocation Unit Size.\n");
    	}
    
    	return 0;
    }
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    /* frequency bases */
    /* divided by 10 to be nice to platforms without floating point */
    
    static const int fbase[] = {
    
    Andy Fleming's avatar
    Andy Fleming committed
    	10000,
    	100000,
    	1000000,
    	10000000,
    };
    
    /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
     * to platforms without floating point.
     */
    
    static const u8 multipliers[] = {
    
    #ifndef CONFIG_DM_MMC_OPS
    
    static void mmc_set_ios(struct mmc *mmc)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    
    	if (mmc->cfg->ops->set_ios)
    		mmc->cfg->ops->set_ios(mmc);
    
    Andy Fleming's avatar
    Andy Fleming committed
    }
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    void mmc_set_clock(struct mmc *mmc, uint clock)
    {
    
    	if (clock > mmc->cfg->f_max)
    		clock = mmc->cfg->f_max;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	if (clock < mmc->cfg->f_min)
    		clock = mmc->cfg->f_min;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	mmc->clock = clock;
    
    	mmc_set_ios(mmc);
    }
    
    
    static void mmc_set_bus_width(struct mmc *mmc, uint width)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    	mmc->bus_width = width;
    
    	mmc_set_ios(mmc);
    }
    
    
    static int mmc_startup(struct mmc *mmc)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    
    Andy Fleming's avatar
    Andy Fleming committed
    	uint mult, freq;
    
    	u64 cmult, csize, capacity;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	struct mmc_cmd cmd;
    
    	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
    	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
    
    	struct blk_desc *bdesc;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    #ifdef CONFIG_MMC_SPI_CRC_ON
    	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
    		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
    		cmd.resp_type = MMC_RSP_R1;
    		cmd.cmdarg = 1;
    		err = mmc_send_cmd(mmc, &cmd, NULL);
    
    		if (err)
    			return err;
    	}
    #endif
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* Put the Card in Identify Mode */
    
    	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
    		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
    
    Andy Fleming's avatar
    Andy Fleming committed
    	cmd.resp_type = MMC_RSP_R2;
    	cmd.cmdarg = 0;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    
    	if (err)
    		return err;
    
    	memcpy(mmc->cid, cmd.response, 16);
    
    	/*
    	 * For MMC cards, set the Relative Address.
    	 * For SD cards, get the Relatvie Address.
    	 * This also puts the cards into Standby State
    	 */
    
    	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
    		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
    		cmd.cmdarg = mmc->rca << 16;
    		cmd.resp_type = MMC_RSP_R6;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    		err = mmc_send_cmd(mmc, &cmd, NULL);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    		if (err)
    			return err;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    		if (IS_SD(mmc))
    			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
    	}
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	/* Get the Card-Specific Data */
    	cmd.cmdidx = MMC_CMD_SEND_CSD;
    	cmd.resp_type = MMC_RSP_R2;
    	cmd.cmdarg = mmc->rca << 16;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    
    
    	/* Waiting for the ready status */
    	mmc_send_status(mmc, timeout);
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	if (err)
    		return err;
    
    
    	mmc->csd[0] = cmd.response[0];
    	mmc->csd[1] = cmd.response[1];
    	mmc->csd[2] = cmd.response[2];
    	mmc->csd[3] = cmd.response[3];
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	if (mmc->version == MMC_VERSION_UNKNOWN) {
    
    		int version = (cmd.response[0] >> 26) & 0xf;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    		switch (version) {
    
    		case 0:
    			mmc->version = MMC_VERSION_1_2;
    			break;
    		case 1:
    			mmc->version = MMC_VERSION_1_4;
    			break;
    		case 2:
    			mmc->version = MMC_VERSION_2_2;
    			break;
    		case 3:
    			mmc->version = MMC_VERSION_3;
    			break;
    		case 4:
    			mmc->version = MMC_VERSION_4;
    			break;
    		default:
    			mmc->version = MMC_VERSION_1_2;
    			break;
    
    Andy Fleming's avatar
    Andy Fleming committed
    		}
    	}
    
    	/* divide frequency by 10, since the mults are 10x bigger */
    
    	freq = fbase[(cmd.response[0] & 0x7)];
    	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	mmc->tran_speed = freq * mult;
    
    
    Markus Niebel's avatar
    Markus Niebel committed
    	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
    
    	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	if (IS_SD(mmc))
    		mmc->write_bl_len = mmc->read_bl_len;
    	else
    
    		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	if (mmc->high_capacity) {
    		csize = (mmc->csd[1] & 0x3f) << 16
    			| (mmc->csd[2] & 0xffff0000) >> 16;
    		cmult = 8;
    	} else {
    		csize = (mmc->csd[1] & 0x3ff) << 2
    			| (mmc->csd[2] & 0xc0000000) >> 30;
    		cmult = (mmc->csd[2] & 0x00038000) >> 15;
    	}
    
    
    	mmc->capacity_user = (csize + 1) << (cmult + 2);
    	mmc->capacity_user *= mmc->read_bl_len;
    	mmc->capacity_boot = 0;
    	mmc->capacity_rpmb = 0;
    	for (i = 0; i < 4; i++)
    		mmc->capacity_gp[i] = 0;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
    		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
    		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    Markus Niebel's avatar
    Markus Niebel committed
    	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
    		cmd.cmdidx = MMC_CMD_SET_DSR;
    		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
    		cmd.resp_type = MMC_RSP_NONE;
    		if (mmc_send_cmd(mmc, &cmd, NULL))
    			printf("MMC: SET_DSR failed\n");
    	}
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* Select the card, and put it into Transfer Mode */
    
    	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
    		cmd.cmdidx = MMC_CMD_SELECT_CARD;
    
    		cmd.resp_type = MMC_RSP_R1;
    
    		cmd.cmdarg = mmc->rca << 16;
    		err = mmc_send_cmd(mmc, &cmd, NULL);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    		if (err)
    			return err;
    	}
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	/*
    	 * For SD, its erase group is always one sector
    	 */
    	mmc->erase_grp_size = 1;
    
    	mmc->part_config = MMCPART_NOAVAILABLE;
    
    	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
    		/* check  ext_csd version and capacity */
    		err = mmc_send_ext_csd(mmc, ext_csd);
    
    		if (err)
    			return err;
    		if (ext_csd[EXT_CSD_REV] >= 2) {
    
    			/*
    			 * According to the JEDEC Standard, the value of
    			 * ext_csd's capacity is valid if the value is more
    			 * than 2GB
    			 */
    
    			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
    					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
    					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
    					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
    
    			capacity *= MMC_MAX_BLOCK_LEN;
    
    			if ((capacity >> 20) > 2 * 1024)
    
    				mmc->capacity_user = capacity;
    
    		switch (ext_csd[EXT_CSD_REV]) {
    		case 1:
    			mmc->version = MMC_VERSION_4_1;
    			break;
    		case 2:
    			mmc->version = MMC_VERSION_4_2;
    			break;
    		case 3:
    			mmc->version = MMC_VERSION_4_3;
    			break;
    		case 5:
    			mmc->version = MMC_VERSION_4_41;
    			break;
    		case 6:
    			mmc->version = MMC_VERSION_4_5;
    			break;
    
    Markus Niebel's avatar
    Markus Niebel committed
    		case 7:
    			mmc->version = MMC_VERSION_5_0;
    			break;
    
    Stefan Wahren's avatar
    Stefan Wahren committed
    		case 8:
    			mmc->version = MMC_VERSION_5_1;
    			break;
    
    		/* The partition data may be non-zero but it is only
    		 * effective if PARTITION_SETTING_COMPLETED is set in
    		 * EXT_CSD, so ignore any data if this bit is not set,
    		 * except for enabling the high-capacity group size
    		 * definition (see below). */
    		part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
    				    EXT_CSD_PARTITION_SETTING_COMPLETED);
    
    
    		/* store the partition info of emmc */
    		mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
    		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
    		    ext_csd[EXT_CSD_BOOT_MULT])
    			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
    
    		if (part_completed &&
    		    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
    
    			mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
    
    		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
    
    		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
    
    		for (i = 0; i < 4; i++) {
    			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
    
    			uint mult = (ext_csd[idx + 2] << 16) +
    
    				(ext_csd[idx + 1] << 8) + ext_csd[idx];
    
    			if (mult)
    				has_parts = true;
    			if (!part_completed)
    				continue;
    			mmc->capacity_gp[i] = mult;
    
    			mmc->capacity_gp[i] *=
    				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
    			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
    
    		if (part_completed) {
    			mmc->enh_user_size =
    				(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
    				(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
    				ext_csd[EXT_CSD_ENH_SIZE_MULT];
    			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
    			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
    			mmc->enh_user_size <<= 19;
    			mmc->enh_user_start =
    				(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
    				(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
    				(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
    				ext_csd[EXT_CSD_ENH_START_ADDR];
    			if (mmc->high_capacity)
    				mmc->enh_user_start <<= 9;
    		}
    
    		 * Host needs to enable ERASE_GRP_DEF bit if device is
    		 * partitioned. This bit will be lost every time after a reset
    		 * or power off. This will affect erase size.
    
    		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
    
    		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
    			has_parts = true;
    		if (has_parts) {
    
    			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
    				EXT_CSD_ERASE_GROUP_DEF, 1);
    
    			if (err)
    				return err;
    
    			else
    				ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
    
    		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
    
    			/* Read out group size from ext_csd */
    
    			mmc->erase_grp_size =
    
    				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
    
    			/*
    			 * if high capacity and partition setting completed
    			 * SEC_COUNT is valid even if it is smaller than 2 GiB
    			 * JEDEC Standard JESD84-B45, 6.2.4
    			 */
    
    			if (mmc->high_capacity && part_completed) {
    
    				capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
    					(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
    					(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
    					(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
    				capacity *= MMC_MAX_BLOCK_LEN;
    				mmc->capacity_user = capacity;
    			}
    
    			/* Calculate the group size from the csd value. */
    
    			int erase_gsz, erase_gmul;
    			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
    			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
    			mmc->erase_grp_size = (erase_gsz + 1)
    				* (erase_gmul + 1);
    		}
    
    
    		mmc->hc_wp_grp_size = 1024
    			* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
    			* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
    
    
    		mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
    
    	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
    
    Andy Fleming's avatar
    Andy Fleming committed
    	if (IS_SD(mmc))
    		err = sd_change_freq(mmc);
    	else
    		err = mmc_change_freq(mmc);
    
    	if (err)
    		return err;
    
    	/* Restrict card's capabilities by what the host can do */
    
    	mmc->card_caps &= mmc->cfg->host_caps;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	if (IS_SD(mmc)) {
    		if (mmc->card_caps & MMC_MODE_4BIT) {
    			cmd.cmdidx = MMC_CMD_APP_CMD;
    			cmd.resp_type = MMC_RSP_R1;
    			cmd.cmdarg = mmc->rca << 16;
    
    			err = mmc_send_cmd(mmc, &cmd, NULL);
    			if (err)
    				return err;
    
    			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
    			cmd.resp_type = MMC_RSP_R1;
    			cmd.cmdarg = 2;
    			err = mmc_send_cmd(mmc, &cmd, NULL);
    			if (err)
    				return err;
    
    			mmc_set_bus_width(mmc, 4);
    		}
    
    
    		err = sd_read_ssr(mmc);
    		if (err)
    			return err;
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    		if (mmc->card_caps & MMC_MODE_HS)
    
    			mmc->tran_speed = 50000000;
    
    Andy Fleming's avatar
    Andy Fleming committed
    		else
    
    			mmc->tran_speed = 25000000;
    
    	} else if (mmc->version >= MMC_VERSION_4) {
    		/* Only version 4 of MMC supports wider bus widths */
    
    		int idx;
    
    		/* An array of possible bus widths in order of preference */
    		static unsigned ext_csd_bits[] = {
    
    			EXT_CSD_DDR_BUS_WIDTH_8,
    			EXT_CSD_DDR_BUS_WIDTH_4,
    
    			EXT_CSD_BUS_WIDTH_8,
    			EXT_CSD_BUS_WIDTH_4,
    			EXT_CSD_BUS_WIDTH_1,
    		};
    
    		/* An array to map CSD bus widths to host cap bits */
    		static unsigned ext_to_hostcaps[] = {
    
    			[EXT_CSD_DDR_BUS_WIDTH_4] =
    				MMC_MODE_DDR_52MHz | MMC_MODE_4BIT,
    			[EXT_CSD_DDR_BUS_WIDTH_8] =
    				MMC_MODE_DDR_52MHz | MMC_MODE_8BIT,
    
    			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
    			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
    		};
    
    		/* An array to map chosen bus width to an integer */
    		static unsigned widths[] = {
    
    			8, 4, 8, 4, 1,
    
    		};
    
    		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
    			unsigned int extw = ext_csd_bits[idx];
    
    			unsigned int caps = ext_to_hostcaps[extw];
    
    			/*
    			 * If the bus width is still not changed,
    			 * don't try to set the default again.
    			 * Otherwise, recover from switch attempts
    			 * by switching to 1-bit bus width.
    			 */
    			if (extw == EXT_CSD_BUS_WIDTH_1 &&
    					mmc->bus_width == 1) {
    				err = 0;
    				break;
    			}
    
    
    			 * Check to make sure the card and controller support
    			 * these capabilities
    
    			if ((mmc->card_caps & caps) != caps)
    
    Andy Fleming's avatar
    Andy Fleming committed
    			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
    
    					EXT_CSD_BUS_WIDTH, extw);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    			if (err)
    
    				continue;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    			mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0;
    
    			mmc_set_bus_width(mmc, widths[idx]);
    
    
    			err = mmc_send_ext_csd(mmc, test_csd);
    
    Mario Schuknecht's avatar
    Mario Schuknecht committed
    			/* Only compare read only fields */
    
    			if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT]
    				== test_csd[EXT_CSD_PARTITIONING_SUPPORT] &&
    			    ext_csd[EXT_CSD_HC_WP_GRP_SIZE]
    				== test_csd[EXT_CSD_HC_WP_GRP_SIZE] &&
    			    ext_csd[EXT_CSD_REV]
    				== test_csd[EXT_CSD_REV] &&
    			    ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
    				== test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] &&
    			    memcmp(&ext_csd[EXT_CSD_SEC_CNT],
    				   &test_csd[EXT_CSD_SEC_CNT], 4) == 0)
    
    				break;
    
    				err = -EBADMSG;
    
    Andy Fleming's avatar
    Andy Fleming committed
    		if (mmc->card_caps & MMC_MODE_HS) {
    			if (mmc->card_caps & MMC_MODE_HS_52MHz)
    
    				mmc->tran_speed = 52000000;
    
    Andy Fleming's avatar
    Andy Fleming committed
    			else
    
    				mmc->tran_speed = 26000000;
    		}
    
    	mmc_set_clock(mmc, mmc->tran_speed);
    
    
    	/* Fix the block length for DDR mode */
    	if (mmc->ddr_mode) {
    		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
    		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
    	}
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* fill in device description */
    
    	bdesc = mmc_get_blk_desc(mmc);
    	bdesc->lun = 0;
    	bdesc->hwpart = 0;
    	bdesc->type = 0;
    	bdesc->blksz = mmc->read_bl_len;
    	bdesc->log2blksz = LOG2(bdesc->blksz);
    	bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
    
    #if !defined(CONFIG_SPL_BUILD) || \
    		(defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
    		!defined(CONFIG_USE_TINY_PRINTF))
    
    	sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
    
    		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
    		(mmc->cid[3] >> 16) & 0xffff);
    
    	sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
    
    		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
    		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
    		(mmc->cid[2] >> 24) & 0xff);
    
    	sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
    
    		(mmc->cid[2] >> 16) & 0xf);
    
    	bdesc->vendor[0] = 0;
    	bdesc->product[0] = 0;
    	bdesc->revision[0] = 0;
    
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
    
    static int mmc_send_if_cond(struct mmc *mmc)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    	struct mmc_cmd cmd;
    	int err;
    
    	cmd.cmdidx = SD_CMD_SEND_IF_COND;
    	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
    
    	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	cmd.resp_type = MMC_RSP_R7;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    
    	if (err)
    		return err;
    
    
    	if ((cmd.response[0] & 0xff) != 0xaa)
    
    		return -EOPNOTSUPP;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	else
    		mmc->version = SD_VERSION_2;
    
    	return 0;
    }
    
    
    /* board-specific MMC power initializations. */
    __weak void board_mmc_power_init(void)
    {
    }
    
    
    Peng Fan's avatar
    Peng Fan committed
    static int mmc_power_init(struct mmc *mmc)
    {
    	board_mmc_power_init();
    
    #if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_REGULATOR) && \
    	!defined(CONFIG_SPL_BUILD)
    	struct udevice *vmmc_supply;
    	int ret;
    
    	ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
    					  &vmmc_supply);
    	if (ret) {
    		puts("No vmmc supply\n");
    		return 0;
    	}
    
    	ret = regulator_set_enable(vmmc_supply, true);
    	if (ret) {
    		puts("Error enabling VMMC supply\n");
    		return ret;
    	}
    #endif
    	return 0;
    }
    
    
    int mmc_start_init(struct mmc *mmc)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	/* we pretend there's no card when init is NULL */
    
    	no_card = mmc_getcd(mmc) == 0;
    #ifndef CONFIG_DM_MMC_OPS
    	no_card = no_card || (mmc->cfg->ops->init == NULL);
    #endif
    	if (no_card) {
    
    		mmc->has_init = 0;
    
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
    
    		printf("MMC: no card present\n");
    
    		return -ENOMEDIUM;
    
    	if (mmc->has_init)
    		return 0;
    
    
    #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
    	mmc_adapter_card_type_ident();
    #endif
    
    Peng Fan's avatar
    Peng Fan committed
    	err = mmc_power_init(mmc);
    	if (err)
    		return err;
    
    #ifdef CONFIG_DM_MMC_OPS
    	/* The device has already been probed ready for use */
    #else
    
    	/* made sure it's not NULL earlier */
    
    	err = mmc->cfg->ops->init(mmc);
    
    Andy Fleming's avatar
    Andy Fleming committed
    	if (err)
    		return err;
    
    	mmc_set_bus_width(mmc, 1);
    	mmc_set_clock(mmc, 1);
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* Reset the Card */
    	err = mmc_go_idle(mmc);
    
    	if (err)
    		return err;
    
    
    	/* The internal partition reset to user partition(0) at every CMD0*/
    
    	mmc_get_blk_desc(mmc)->hwpart = 0;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* Test for SD version 2 */
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	/* Now try to get the SD card's operating condition */
    	err = sd_send_op_cond(mmc);
    
    	/* If the command timed out, we check for an MMC card */
    
    	if (err == -ETIMEDOUT) {
    
    Andy Fleming's avatar
    Andy Fleming committed
    		err = mmc_send_op_cond(mmc);
    
    
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
    
    Andy Fleming's avatar
    Andy Fleming committed
    			printf("Card did not respond to voltage select!\n");
    
    			return -EOPNOTSUPP;
    
    		mmc->init_in_progress = 1;
    
    	return err;
    }
    
    static int mmc_complete_init(struct mmc *mmc)
    {
    	int err = 0;
    
    
    	mmc->init_in_progress = 0;
    
    	if (mmc->op_cond_pending)
    		err = mmc_complete_op_cond(mmc);
    
    	if (!err)
    		err = mmc_startup(mmc);
    
    	if (err)
    		mmc->has_init = 0;
    	else
    		mmc->has_init = 1;
    
    	return err;
    }
    
    int mmc_init(struct mmc *mmc)
    {
    
    #ifdef CONFIG_DM_MMC
    	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
    
    	if (mmc->has_init)
    		return 0;
    
    	if (!mmc->init_in_progress)
    		err = mmc_start_init(mmc);
    
    
    		err = mmc_complete_init(mmc);
    	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
    
    Markus Niebel's avatar
    Markus Niebel committed
    int mmc_set_dsr(struct mmc *mmc, u16 val)
    {
    	mmc->dsr = val;
    	return 0;
    }
    
    
    Jeroen Hofstee's avatar
    Jeroen Hofstee committed
    /* CPU-specific MMC initializations */
    __weak int cpu_mmc_init(bd_t *bis)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    	return -1;
    }
    
    
    Jeroen Hofstee's avatar
    Jeroen Hofstee committed
    /* board-specific MMC initializations. */
    __weak int board_mmc_init(bd_t *bis)
    {
    	return -1;
    }
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    void mmc_set_preinit(struct mmc *mmc, int preinit)
    {
    	mmc->preinit = preinit;
    }
    
    
    #if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD)
    static int mmc_probe(bd_t *bis)
    {
    	return 0;
    }
    #elif defined(CONFIG_DM_MMC)
    static int mmc_probe(bd_t *bis)
    {
    
    	struct uclass *uc;
    
    	struct udevice *dev;
    
    
    	ret = uclass_get(UCLASS_MMC, &uc);
    	if (ret)
    		return ret;
    
    
    	/*
    	 * Try to add them in sequence order. Really with driver model we
    	 * should allow holes, but the current MMC list does not allow that.
    	 * So if we request 0, 1, 3 we will get 0, 1, 2.
    	 */
    	for (i = 0; ; i++) {
    		ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
    		if (ret == -ENODEV)
    			break;
    	}
    	uclass_foreach_dev(dev, uc) {
    		ret = device_probe(dev);
    
    			printf("%s - probe failed: %d\n", dev->name, ret);
    
    	}
    
    	return 0;
    }
    #else
    static int mmc_probe(bd_t *bis)
    {
    	if (board_mmc_init(bis) < 0)
    		cpu_mmc_init(bis);
    
    	return 0;
    }
    #endif
    
    Andy Fleming's avatar
    Andy Fleming committed
    int mmc_initialize(bd_t *bis)
    {
    
    	if (initialized)	/* Avoid initializing mmc multiple times */
    		return 0;
    	initialized = 1;
    
    
    #ifndef CONFIG_BLK
    	mmc_list_init();
    #endif
    
    	ret = mmc_probe(bis);
    	if (ret)
    		return ret;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    #ifndef CONFIG_SPL_BUILD
    
    Andy Fleming's avatar
    Andy Fleming committed
    	print_mmc_devices(',');
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	return 0;
    }