diff options
Diffstat (limited to 'drivers/mmc/mmc_write.c')
| -rw-r--r-- | drivers/mmc/mmc_write.c | 179 | 
1 files changed, 179 insertions, 0 deletions
| diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c new file mode 100644 index 000000000..aa2fdefa7 --- /dev/null +++ b/drivers/mmc/mmc_write.c @@ -0,0 +1,179 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <part.h> +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ +	struct mmc_cmd cmd; +	ulong end; +	int err, start_cmd, end_cmd; + +	if (mmc->high_capacity) { +		end = start + blkcnt - 1; +	} else { +		end = (start + blkcnt - 1) * mmc->write_bl_len; +		start *= mmc->write_bl_len; +	} + +	if (IS_SD(mmc)) { +		start_cmd = SD_CMD_ERASE_WR_BLK_START; +		end_cmd = SD_CMD_ERASE_WR_BLK_END; +	} else { +		start_cmd = MMC_CMD_ERASE_GROUP_START; +		end_cmd = MMC_CMD_ERASE_GROUP_END; +	} + +	cmd.cmdidx = start_cmd; +	cmd.cmdarg = start; +	cmd.resp_type = MMC_RSP_R1; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	cmd.cmdidx = end_cmd; +	cmd.cmdarg = end; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	cmd.cmdidx = MMC_CMD_ERASE; +	cmd.cmdarg = SECURE_ERASE; +	cmd.resp_type = MMC_RSP_R1b; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	return 0; + +err_out: +	puts("mmc erase failed\n"); +	return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ +	int err = 0; +	struct mmc *mmc = find_mmc_device(dev_num); +	lbaint_t blk = 0, blk_r = 0; +	int timeout = 1000; + +	if (!mmc) +		return -1; + +	if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) +		printf("\n\nCaution! Your devices Erase group is 0x%x\n" +		       "The erase range would be change to " +		       "0x" LBAF "~0x" LBAF "\n\n", +		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), +		       ((start + blkcnt + mmc->erase_grp_size) +		       & ~(mmc->erase_grp_size - 1)) - 1); + +	while (blk < blkcnt) { +		blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? +			mmc->erase_grp_size : (blkcnt - blk); +		err = mmc_erase_t(mmc, start + blk, blk_r); +		if (err) +			break; + +		blk += blk_r; + +		/* Waiting for the ready status */ +		if (mmc_send_status(mmc, timeout)) +			return 0; +	} + +	return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, +		lbaint_t blkcnt, const void *src) +{ +	struct mmc_cmd cmd; +	struct mmc_data data; +	int timeout = 1000; + +	if ((start + blkcnt) > mmc->block_dev.lba) { +		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", +		       start + blkcnt, mmc->block_dev.lba); +		return 0; +	} + +	if (blkcnt == 0) +		return 0; +	else if (blkcnt == 1) +		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; +	else +		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + +	if (mmc->high_capacity) +		cmd.cmdarg = start; +	else +		cmd.cmdarg = start * mmc->write_bl_len; + +	cmd.resp_type = MMC_RSP_R1; + +	data.src = src; +	data.blocks = blkcnt; +	data.blocksize = mmc->write_bl_len; +	data.flags = MMC_DATA_WRITE; + +	if (mmc_send_cmd(mmc, &cmd, &data)) { +		printf("mmc write failed\n"); +		return 0; +	} + +	/* SPI multiblock writes terminate using a special +	 * token, not a STOP_TRANSMISSION request. +	 */ +	if (!mmc_host_is_spi(mmc) && blkcnt > 1) { +		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; +		cmd.cmdarg = 0; +		cmd.resp_type = MMC_RSP_R1b; +		if (mmc_send_cmd(mmc, &cmd, NULL)) { +			printf("mmc fail to send stop cmd\n"); +			return 0; +		} +	} + +	/* Waiting for the ready status */ +	if (mmc_send_status(mmc, timeout)) +		return 0; + +	return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ +	lbaint_t cur, blocks_todo = blkcnt; + +	struct mmc *mmc = find_mmc_device(dev_num); +	if (!mmc) +		return 0; + +	if (mmc_set_blocklen(mmc, mmc->write_bl_len)) +		return 0; + +	do { +		cur = (blocks_todo > mmc->b_max) ?  mmc->b_max : blocks_todo; +		if (mmc_write_blocks(mmc, start, cur, src) != cur) +			return 0; +		blocks_todo -= cur; +		start += cur; +		src += cur * mmc->write_bl_len; +	} while (blocks_todo > 0); + +	return blkcnt; +} |