Skip to content
Snippets Groups Projects
mmc.c 33.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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;
    		}
    	}
    
    	/* 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;
    
    
    	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
    
    	/* 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 && (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;
    		}
    
    
    		/*
    		 * 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] *
    					MMC_MAX_BLOCK_LEN * 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) ||
    		    ext_csd[EXT_CSD_BOOT_MULT])
    
    			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
    
    
    		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;
    			mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
    				(ext_csd[idx + 1] << 8) + ext_csd[idx];
    			mmc->capacity_gp[i] *=
    				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
    			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
    		}
    
    	err = mmc_set_capacity(mmc, mmc->part_num);
    	if (err)
    		return err;
    
    
    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;
    
    			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;
    
    Andy Fleming's avatar
    Andy Fleming committed
    	} else {
    
    		int idx;
    
    		/* An array of possible bus widths in order of preference */
    		static unsigned ext_csd_bits[] = {
    			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_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, 1,
    		};
    
    		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
    			unsigned int extw = ext_csd_bits[idx];
    
    			/*
    			 * Check to make sure the controller supports
    			 * this bus width, if it's more than 1
    			 */
    			if (extw != EXT_CSD_BUS_WIDTH_1 &&
    					!(mmc->host_caps & ext_to_hostcaps[extw]))
    				continue;
    
    
    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_set_bus_width(mmc, widths[idx]);
    
    
    			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 |= ext_to_hostcaps[extw];
    
    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);
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    	/* 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.log2blksz = LOG2(mmc->block_dev.blksz);
    
    	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
    
    	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
    		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
    		(mmc->cid[3] >> 16) & 0xffff);
    	sprintf(mmc->block_dev.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(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
    		(mmc->cid[2] >> 16) & 0xf);
    
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
    
    Andy Fleming's avatar
    Andy Fleming committed
    	init_part(&mmc->block_dev);
    
    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->voltages & 0xff8000) != 0) << 8 | 0xaa;
    	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;
    }
    
    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);
    
    	if (!mmc || mmc_init(mmc))
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    Andy Fleming's avatar
    Andy Fleming committed
    }
    
    Andy Fleming's avatar
    Andy Fleming committed
    
    
    int mmc_start_init(struct mmc *mmc)
    
    Andy Fleming's avatar
    Andy Fleming committed
    {
    
    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 && err != IN_PROGRESS) {
    
    Andy Fleming's avatar
    Andy Fleming committed
    			printf("Card did not respond to voltage select!\n");
    			return UNUSABLE_ERR;
    		}
    	}
    
    
    	if (err == IN_PROGRESS)
    		mmc->init_in_progress = 1;
    
    	return err;
    }
    
    static int mmc_complete_init(struct mmc *mmc)
    {
    	int err = 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;
    
    	mmc->init_in_progress = 0;
    	return err;
    }
    
    int mmc_init(struct mmc *mmc)
    {
    	int err = IN_PROGRESS;
    	unsigned start = get_timer(0);
    
    	if (mmc->has_init)
    		return 0;
    	if (!mmc->init_in_progress)
    		err = mmc_start_init(mmc);
    
    	if (!err || err == IN_PROGRESS)
    		err = mmc_complete_init(mmc);
    	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
    
    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;
    }
    
    
    void mmc_set_preinit(struct mmc *mmc, int preinit)
    {
    	mmc->preinit = preinit;
    }
    
    static void do_preinit(void)
    {
    	struct mmc *m;
    	struct list_head *entry;
    
    	list_for_each(entry, &mmc_devices) {
    		m = list_entry(entry, struct mmc, link);
    
    		if (m->preinit)
    			mmc_start_init(m);
    	}
    }
    
    
    
    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);
    
    
    #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;
    }
    
    /*
     * This function shall form and send the commands to open / close the
     * boot partition specified by user.
     *
     * Input Parameters:
     * ack: 0x0 - No boot acknowledge sent (default)
     *	0x1 - Boot acknowledge sent during boot operation
     * part_num: User selects boot data that will be sent to master
     *	0x0 - Device not boot enabled (default)
     *	0x1 - Boot partition 1 enabled for boot
     *	0x2 - Boot partition 2 enabled for boot
     * access: User selects partitions to access
     *	0x0 : No access to boot partition (default)
     *	0x1 : R/W boot partition 1
     *	0x2 : R/W boot partition 2
     *	0x3 : R/W Replay Protected Memory Block (RPMB)
     *
     * Returns 0 on success.
     */
    int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
    {
    	int err;
    	struct mmc_cmd cmd;
    
    	/* Boot ack enable, boot partition enable , boot partition access */
    	cmd.cmdidx = MMC_CMD_SWITCH;
    	cmd.resp_type = MMC_RSP_R1b;
    
    	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
    			(EXT_CSD_PART_CONF << 16) |
    			((EXT_CSD_BOOT_ACK(ack) |
    			EXT_CSD_BOOT_PART_NUM(part_num) |
    			EXT_CSD_PARTITION_ACCESS(access)) << 8);
    
    	err = mmc_send_cmd(mmc, &cmd, NULL);
    	if (err) {
    		if (access) {
    			debug("mmc boot partition#%d open fail:Error1 = %d\n",
    			      part_num, err);
    		} else {
    			debug("mmc boot partition#%d close fail:Error = %d\n",
    			      part_num, err);
    		}
    		return err;
    	}
    
    	if (access) {
    		/* 4bit transfer mode at booting time. */
    		cmd.cmdidx = MMC_CMD_SWITCH;
    		cmd.resp_type = MMC_RSP_R1b;
    
    		cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
    				(EXT_CSD_BOOT_BUS_WIDTH << 16) |
    				((1 << 0) << 8);
    
    		err = mmc_send_cmd(mmc, &cmd, NULL);
    		if (err) {
    			debug("mmc boot partition#%d open fail:Error2 = %d\n",
    			      part_num, err);
    			return err;
    		}
    	}
    	return 0;
    }
    #endif