Skip to content
Snippets Groups Projects
mmc.c 26.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andy Fleming's avatar
    Andy Fleming committed
    	}
    
    	mmc->capacity = (csize + 1) << (cmult + 2);
    	mmc->capacity *= mmc->read_bl_len;
    
    	if (mmc->read_bl_len > 512)
    		mmc->read_bl_len = 512;
    
    	if (mmc->write_bl_len > 512)
    		mmc->write_bl_len = 512;
    
    	/* 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;
    		cmd.flags = 0;
    		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 & (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;
    
    			if ((capacity >> 20) > 2 * 1024)
    
    				mmc->capacity = capacity;
    
    		/*
    		 * Check whether GROUP_DEF is set, if yes, read out
    		 * group size from ext_csd directly, or calculate
    		 * the group size from the csd value.
    		 */
    
    		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF])
    			mmc->erase_grp_size =
    			      ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 512 * 1024;
    
    		else {
    			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);
    		}
    
    
    		/* store the partition info of emmc */
    
    		if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT)
    			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
    
    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->host_caps;
    
    	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;
    			cmd.flags = 0;
    
    			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;
    			cmd.flags = 0;
    			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_set_clock(mmc, 50000000);
    		else
    			mmc_set_clock(mmc, 25000000);
    	} else {
    
    		for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) {
    
    Andy Fleming's avatar
    Andy Fleming committed
    			/* Set the card to use 4 bit*/
    			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
    
    					EXT_CSD_BUS_WIDTH, width);
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    			if (err)
    
    				continue;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    			if (!width) {
    				mmc_set_bus_width(mmc, 1);
    				break;
    			} else
    				mmc_set_bus_width(mmc, 4 * width);
    
    			err = mmc_send_ext_csd(mmc, test_csd);
    			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \
    				    == test_csd[EXT_CSD_PARTITIONING_SUPPORT]
    				 && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \
    				    == test_csd[EXT_CSD_ERASE_GROUP_DEF] \
    				 && 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) {
    
    				mmc->card_caps |= width;
    				break;
    			}
    
    Andy Fleming's avatar
    Andy Fleming committed
    		}
    
    		if (mmc->card_caps & MMC_MODE_HS) {
    			if (mmc->card_caps & MMC_MODE_HS_52MHz)
    				mmc_set_clock(mmc, 52000000);
    			else
    				mmc_set_clock(mmc, 26000000);
    		} else
    			mmc_set_clock(mmc, 20000000);
    	}
    
    	/* fill in device description */
    	mmc->block_dev.lun = 0;
    	mmc->block_dev.type = 0;
    	mmc->block_dev.blksz = mmc->read_bl_len;
    
    	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
    
    	sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8,
    			(mmc->cid[2] << 8) | (mmc->cid[3] >> 24));
    	sprintf(mmc->block_dev.product, "%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);
    	sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28,
    			(mmc->cid[2] >> 24) & 0xf);
    
    Andy Fleming's avatar
    Andy Fleming committed
    	init_part(&mmc->block_dev);
    
    	return 0;
    }
    
    int mmc_send_if_cond(struct mmc *mmc)
    {
    	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->voltages & 0xff8000) != 0) << 8 | 0xaa;
    	cmd.resp_type = MMC_RSP_R7;
    	cmd.flags = 0;
    
    	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;
    }
    
    int mmc_register(struct mmc *mmc)
    {
    	/* Setup the universal parts of the block interface just once */
    	mmc->block_dev.if_type = IF_TYPE_MMC;
    	mmc->block_dev.dev = cur_dev_num++;
    	mmc->block_dev.removable = 1;
    	mmc->block_dev.block_read = mmc_bread;
    	mmc->block_dev.block_write = mmc_bwrite;
    
    	mmc->block_dev.block_erase = mmc_berase;
    
    	if (!mmc->b_max)
    		mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    	INIT_LIST_HEAD (&mmc->link);
    
    	list_add_tail (&mmc->link, &mmc_devices);
    
    	return 0;
    }
    
    
    #ifdef CONFIG_PARTITIONS
    
    Andy Fleming's avatar
    Andy Fleming committed
    block_dev_desc_t *mmc_get_dev(int dev)
    {
    	struct mmc *mmc = find_mmc_device(dev);
    
    
    	return mmc ? &mmc->block_dev : NULL;
    
    Andy Fleming's avatar
    Andy Fleming committed
    }
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    int mmc_init(struct mmc *mmc)
    {
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    	if (mmc_getcd(mmc) == 0) {
    		mmc->has_init = 0;
    		printf("MMC: no card present\n");
    		return NO_CARD_ERR;
    	}
    
    
    	if (mmc->has_init)
    		return 0;
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	err = mmc->init(mmc);
    
    	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->part_num = 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 (err) {
    			printf("Card did not respond to voltage select!\n");
    			return UNUSABLE_ERR;
    		}
    	}
    
    
    	err = mmc_startup(mmc);
    	if (err)
    		mmc->has_init = 0;
    	else
    		mmc->has_init = 1;
    	return err;
    
    Andy Fleming's avatar
    Andy Fleming committed
    }
    
    /*
     * CPU and board-specific MMC initializations.  Aliased function
     * signals caller to move on
     */
    static int __def_mmc_init(bd_t *bis)
    {
    	return -1;
    }
    
    
    int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
    int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    void print_mmc_devices(char separator)
    {
    	struct mmc *m;
    	struct list_head *entry;
    
    	list_for_each(entry, &mmc_devices) {
    		m = list_entry(entry, struct mmc, link);
    
    		printf("%s: %d", m->name, m->block_dev.dev);
    
    		if (entry->next != &mmc_devices)
    			printf("%c ", separator);
    	}
    
    	printf("\n");
    }
    
    
    int get_mmc_num(void)
    {
    	return cur_dev_num;
    }
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    int mmc_initialize(bd_t *bis)
    {
    	INIT_LIST_HEAD (&mmc_devices);
    	cur_dev_num = 0;
    
    	if (board_mmc_init(bis) < 0)
    		cpu_mmc_init(bis);
    
    	print_mmc_devices(',');
    
    	return 0;
    }