diff options
Diffstat (limited to 'roms/u-boot/drivers/mmc/mmc_write.c')
-rw-r--r-- | roms/u-boot/drivers/mmc/mmc_write.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/mmc/mmc_write.c b/roms/u-boot/drivers/mmc/mmc_write.c new file mode 100644 index 000000000..d23b7d972 --- /dev/null +++ b/roms/u-boot/drivers/mmc/mmc_write.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + */ + +#include <config.h> +#include <common.h> +#include <blk.h> +#include <dm.h> +#include <part.h> +#include <div64.h> +#include <linux/math64.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 = MMC_ERASE_ARG; + 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; +} + +#if CONFIG_IS_ENABLED(BLK) +ulong mmc_berase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt) +#else +ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) +#endif +{ +#if CONFIG_IS_ENABLED(BLK) + struct blk_desc *block_dev = dev_get_uclass_plat(dev); +#endif + int dev_num = block_dev->devnum; + int err = 0; + u32 start_rem, blkcnt_rem; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + int timeout_ms = 1000; + + if (!mmc) + return -1; + + err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, + block_dev->hwpart); + if (err < 0) + return -1; + + /* + * We want to see if the requested start or total block count are + * unaligned. We discard the whole numbers and only care about the + * remainder. + */ + err = div_u64_rem(start, mmc->erase_grp_size, &start_rem); + err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem); + if (start_rem || blkcnt_rem) + 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) { + if (IS_SD(mmc) && mmc->ssr.au) { + blk_r = ((blkcnt - blk) > mmc->ssr.au) ? + mmc->ssr.au : (blkcnt - blk); + } else { + 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_poll_for_busy(mmc, timeout_ms)) + 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_ms = 1000; + + if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) { + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc_get_blk_desc(mmc)->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_poll_for_busy(mmc, timeout_ms)) + return 0; + + return blkcnt; +} + +#if CONFIG_IS_ENABLED(BLK) +ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, + const void *src) +#else +ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, + const void *src) +#endif +{ +#if CONFIG_IS_ENABLED(BLK) + struct blk_desc *block_dev = dev_get_uclass_plat(dev); +#endif + int dev_num = block_dev->devnum; + lbaint_t cur, blocks_todo = blkcnt; + int err; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart); + if (err < 0) + return 0; + + if (mmc_set_blocklen(mmc, mmc->write_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->cfg->b_max) ? + mmc->cfg->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; +} |