mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 22:20:45 +00:00
6cf8a903c5
It's quite hard to figure out time units for various function that have timeout parameters. This leads to possible errors when one forgets to convert ms to us, for example. Let's rename those parameters correspondingly to 'timeout_us' and 'timeout_ms' to prevent such issues further. While at it, add time units info as comments to struct mmc fields. This commit doesn't change the behavior, only renames parameters names. Buildman should report no changes at all. Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> Reviewed-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Igor Opaniuk <igor.opaniuk@gmail.com>
223 lines
5 KiB
C
223 lines
5 KiB
C
// 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 <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_platdata(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_platdata(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;
|
|
}
|