Skip to content
Snippets Groups Projects
mmc.c 42.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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
    }
    
    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);
    		}
    
    		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;
    
    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)
    
    Andy Fleming's avatar
    Andy Fleming committed
    		return UNUSABLE_ERR;
    	else
    		mmc->version = SD_VERSION_2;
    
    	return 0;
    }
    
    
    #ifdef CONFIG_BLK
    int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg)
    {
    	struct blk_desc *bdesc;
    	struct udevice *bdev;
    	int ret;
    
    	ret = blk_create_devicef(dev, "mmc_blk", "blk", IF_TYPE_MMC, -1, 512,
    				 0, &bdev);
    	if (ret) {
    		debug("Cannot create block device\n");
    		return ret;
    	}
    	bdesc = dev_get_uclass_platdata(bdev);
    	mmc->cfg = cfg;
    	mmc->priv = dev;
    
    	/* the following chunk was from mmc_register() */
    
    	/* Setup dsr related values */
    	mmc->dsr_imp = 0;
    	mmc->dsr = 0xffffffff;
    	/* Setup the universal parts of the block interface just once */
    	bdesc->removable = 1;
    
    	/* setup initial part type */
    
    	bdesc->part_type = cfg->part_type;
    
    	mmc->dev = dev;
    
    	return 0;
    }
    
    int mmc_unbind(struct udevice *dev)
    {
    	struct udevice *bdev;
    
    	device_find_first_child(dev, &bdev);
    	if (bdev) {
    		device_remove(bdev);
    		device_unbind(bdev);
    	}
    
    	return 0;
    }
    
    #else
    
    struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    
    	struct blk_desc *bdesc;
    
    	struct mmc *mmc;
    
    	/* quick validation */
    	if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
    			cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
    		return NULL;
    
    	mmc = calloc(1, sizeof(*mmc));
    	if (mmc == NULL)
    		return NULL;
    
    	mmc->cfg = cfg;
    	mmc->priv = priv;
    
    	/* the following chunk was mmc_register() */
    
    
    Markus Niebel's avatar
    Markus Niebel committed
    	/* Setup dsr related values */
    	mmc->dsr_imp = 0;
    	mmc->dsr = 0xffffffff;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* Setup the universal parts of the block interface just once */
    
    	bdesc = mmc_get_blk_desc(mmc);
    	bdesc->if_type = IF_TYPE_MMC;
    	bdesc->removable = 1;
    	bdesc->devnum = mmc_get_next_devnum();
    	bdesc->block_read = mmc_bread;
    	bdesc->block_write = mmc_bwrite;
    	bdesc->block_erase = mmc_berase;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	/* setup initial part type */
    
    	bdesc->part_type = mmc->cfg->part_type;
    	mmc_list_add(mmc);
    
    
    	return mmc;
    }
    
    void mmc_destroy(struct mmc *mmc)
    {
    	/* only freeing memory for now */
    	free(mmc);
    
    Andy Fleming's avatar
    Andy Fleming committed
    }
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    static int mmc_get_dev(int dev, struct blk_desc **descp)
    
    {
    	struct mmc *mmc = find_mmc_device(dev);
    	int ret;
    
    	if (!mmc)
    		return -ENODEV;
    	ret = mmc_init(mmc);
    	if (ret)
    		return ret;
    
    	*descp = &mmc->block_dev;
    
    	return 0;
    }
    
    /* board-specific MMC power initializations. */
    __weak void board_mmc_power_init(void)
    {
    }
    
    
    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 */
    
    	if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
    
    		mmc->has_init = 0;
    
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
    
    		printf("MMC: no card present\n");
    
    		return NO_CARD_ERR;
    	}
    
    
    	if (mmc->has_init)
    		return 0;
    
    
    #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
    	mmc_adapter_card_type_ident();
    #endif
    
    	/* 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 == TIMEOUT) {
    		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");
    
    Andy Fleming's avatar
    Andy Fleming committed
    			return UNUSABLE_ERR;
    		}
    	}
    
    
    		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;
    }
    
    
    #ifdef CONFIG_SUPPORT_EMMC_BOOT
    /*
     * This function changes the size of boot partition and the size of rpmb
     * partition present on EMMC devices.
     *
     * Input Parameters:
     * struct *mmc: pointer for the mmc device strcuture
     * bootsize: size of boot partition
     * rpmbsize: size of rpmb partition
     *
     * Returns 0 on success.
     */
    
    int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
    				unsigned long rpmbsize)
    {
    	int err;
    	struct mmc_cmd cmd;
    
    	/* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
    	cmd.cmdidx = MMC_CMD_RES_MAN;
    	cmd.resp_type = MMC_RSP_R1b;
    	cmd.cmdarg = MMC_CMD62_ARG1;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    	if (err) {
    		debug("mmc_boot_partition_size_change: Error1 = %d\n", err);
    		return err;
    	}
    
    	/* Boot partition changing mode */
    	cmd.cmdidx = MMC_CMD_RES_MAN;
    	cmd.resp_type = MMC_RSP_R1b;
    	cmd.cmdarg = MMC_CMD62_ARG2;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    	if (err) {
    		debug("mmc_boot_partition_size_change: Error2 = %d\n", err);
    		return err;
    	}
    	/* boot partition size is multiple of 128KB */
    	bootsize = (bootsize * 1024) / 128;
    
    	/* Arg: boot partition size */
    	cmd.cmdidx = MMC_CMD_RES_MAN;
    	cmd.resp_type = MMC_RSP_R1b;
    	cmd.cmdarg = bootsize;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    	if (err) {
    		debug("mmc_boot_partition_size_change: Error3 = %d\n", err);
    		return err;
    	}
    	/* RPMB partition size is multiple of 128KB */
    	rpmbsize = (rpmbsize * 1024) / 128;
    	/* Arg: RPMB partition size */
    	cmd.cmdidx = MMC_CMD_RES_MAN;
    	cmd.resp_type = MMC_RSP_R1b;
    	cmd.cmdarg = rpmbsize;
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    	if (err) {
    		debug("mmc_boot_partition_size_change: Error4 = %d\n", err);
    		return err;
    	}
    	return 0;
    }
    
    
    /*
     * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH
     * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH
     * and BOOT_MODE.
     *
     * Returns 0 on success.
     */
    int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode)
    {
    	int err;
    
    	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH,
    			 EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) |
    			 EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) |
    			 EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width));
    
    	if (err)
    		return err;
    	return 0;
    }
    
    
    /*
     * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG)
     * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and
     * PARTITION_ACCESS.
     *
     * Returns 0 on success.
     */
    int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
    {
    	int err;
    
    	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
    			 EXT_CSD_BOOT_ACK(ack) |
    			 EXT_CSD_BOOT_PART_NUM(part_num) |
    			 EXT_CSD_PARTITION_ACCESS(access));
    
    	if (err)
    		return err;
    	return 0;
    }
    
    
    /*
     * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value
     * for enable.  Note that this is a write-once field for non-zero values.
     *
     * Returns 0 on success.
     */
    int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
    {
    	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION,
    			  enable);
    }
    
    #ifdef CONFIG_BLK
    static const struct blk_ops mmc_blk_ops = {
    	.read	= mmc_bread,
    	.write	= mmc_bwrite,
    	.select_hwpart	= mmc_select_hwpart,
    };
    
    U_BOOT_DRIVER(mmc_blk) = {
    	.name		= "mmc_blk",
    	.id		= UCLASS_BLK,
    	.ops		= &mmc_blk_ops,
    };
    #else
    
    U_BOOT_LEGACY_BLK(mmc) = {
    	.if_typename	= "mmc",
    	.if_type	= IF_TYPE_MMC,
    	.max_devs	= -1,
    
    	.get_dev	= mmc_get_dev,
    
    	.select_hwpart	= mmc_select_hwpartp,