diff options
Diffstat (limited to 'drivers/mmc/mmc.c')
| -rw-r--r-- | drivers/mmc/mmc.c | 271 | 
1 files changed, 239 insertions, 32 deletions
| diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2590f1bcce5..a492bbb41f5 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -524,48 +524,70 @@ static int sd_send_op_cond(struct mmc *mmc)  	return 0;  } -static int mmc_send_op_cond(struct mmc *mmc) +/* We pass in the cmd since otherwise the init seems to fail */ +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, +		int use_arg)  { -	int timeout = 10000; -	struct mmc_cmd cmd;  	int err; +	cmd->cmdidx = MMC_CMD_SEND_OP_COND; +	cmd->resp_type = MMC_RSP_R3; +	cmd->cmdarg = 0; +	if (use_arg && !mmc_host_is_spi(mmc)) { +		cmd->cmdarg = +			(mmc->voltages & +			(mmc->op_cond_response & OCR_VOLTAGE_MASK)) | +			(mmc->op_cond_response & OCR_ACCESS_MODE); + +		if (mmc->host_caps & MMC_MODE_HC) +			cmd->cmdarg |= OCR_HCS; +	} +	err = mmc_send_cmd(mmc, cmd, NULL); +	if (err) +		return err; +	mmc->op_cond_response = cmd->response[0]; +	return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ +	struct mmc_cmd cmd; +	int err, i; +  	/* Some cards seem to need this */  	mmc_go_idle(mmc);   	/* Asking to the card its capabilities */ - 	cmd.cmdidx = MMC_CMD_SEND_OP_COND; - 	cmd.resp_type = MMC_RSP_R3; - 	cmd.cmdarg = 0; - - 	err = mmc_send_cmd(mmc, &cmd, NULL); +	mmc->op_cond_pending = 1; +	for (i = 0; i < 2; i++) { +		err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); +		if (err) +			return err; - 	if (err) - 		return err; +		/* exit if not busy (flag seems to be inverted) */ +		if (mmc->op_cond_response & OCR_BUSY) +			return 0; +	} +	return IN_PROGRESS; +} - 	udelay(1000); +int mmc_complete_op_cond(struct mmc *mmc) +{ +	struct mmc_cmd cmd; +	int timeout = 1000; +	uint start; +	int err; +	mmc->op_cond_pending = 0; +	start = get_timer(0);  	do { -		cmd.cmdidx = MMC_CMD_SEND_OP_COND; -		cmd.resp_type = MMC_RSP_R3; -		cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : -				(mmc->voltages & -				(cmd.response[0] & OCR_VOLTAGE_MASK)) | -				(cmd.response[0] & OCR_ACCESS_MODE)); - -		if (mmc->host_caps & MMC_MODE_HC) -			cmd.cmdarg |= OCR_HCS; - -		err = mmc_send_cmd(mmc, &cmd, NULL); - +		err = mmc_send_op_cond_iter(mmc, &cmd, 1);  		if (err)  			return err; - -		udelay(1000); -	} while (!(cmd.response[0] & OCR_BUSY) && timeout--); - -	if (timeout <= 0) -		return UNUSABLE_ERR; +		if (get_timer(start) > timeout) +			return UNUSABLE_ERR; +		udelay(100); +	} while (!(mmc->op_cond_response & OCR_BUSY));  	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */  		cmd.cmdidx = MMC_CMD_SPI_READ_OCR; @@ -1274,7 +1296,7 @@ block_dev_desc_t *mmc_get_dev(int dev)  }  #endif -int mmc_init(struct mmc *mmc) +int mmc_start_init(struct mmc *mmc)  {  	int err; @@ -1314,17 +1336,48 @@ int mmc_init(struct mmc *mmc)  	if (err == TIMEOUT) {  		err = mmc_send_op_cond(mmc); -		if (err) { +		if (err && err != IN_PROGRESS) {  			printf("Card did not respond to voltage select!\n");  			return UNUSABLE_ERR;  		}  	} -	err = mmc_startup(mmc); +	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));  	return err;  } @@ -1362,6 +1415,25 @@ 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); +	} +} + +  int mmc_initialize(bd_t *bis)  {  	INIT_LIST_HEAD (&mmc_devices); @@ -1372,5 +1444,140 @@ int mmc_initialize(bd_t *bis)  	print_mmc_devices(','); +	do_preinit(); +	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 | 
