mmc: erase: Use TRIM erase when available

The default erase command applies on erase group unit, and
simply round down to erase group size. When the start block
is not aligned to erase group size (e.g. erasing partition)
it causes unwanted erasing of the previous blocks, part of
the same erase group (e.g. owned by other logical partition,
or by the partition table itself).

To prevent this issue, a simple solution is to use TRIM as
argument of the Erase command, which is usually supported
with eMMC > 4.0, and allow to apply erase operation to write
blocks instead of erase group

Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Loic Poulain 2023-01-26 10:24:18 +01:00 committed by Jaehoon Chung
parent eeb739a6fd
commit 67642c1254

View file

@ -15,7 +15,7 @@
#include <linux/math64.h> #include <linux/math64.h>
#include "mmc_private.h" #include "mmc_private.h"
static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt, u32 args)
{ {
struct mmc_cmd cmd; struct mmc_cmd cmd;
ulong end; ulong end;
@ -52,7 +52,7 @@ static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
goto err_out; goto err_out;
cmd.cmdidx = MMC_CMD_ERASE; cmd.cmdidx = MMC_CMD_ERASE;
cmd.cmdarg = MMC_ERASE_ARG; cmd.cmdarg = args ? args : MMC_ERASE_ARG;
cmd.resp_type = MMC_RSP_R1b; cmd.resp_type = MMC_RSP_R1b;
err = mmc_send_cmd(mmc, &cmd, NULL); err = mmc_send_cmd(mmc, &cmd, NULL);
@ -77,7 +77,7 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt)
#endif #endif
int dev_num = block_dev->devnum; int dev_num = block_dev->devnum;
int err = 0; int err = 0;
u32 start_rem, blkcnt_rem; u32 start_rem, blkcnt_rem, erase_args = 0;
struct mmc *mmc = find_mmc_device(dev_num); struct mmc *mmc = find_mmc_device(dev_num);
lbaint_t blk = 0, blk_r = 0; lbaint_t blk = 0, blk_r = 0;
int timeout_ms = 1000; int timeout_ms = 1000;
@ -97,13 +97,25 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt)
*/ */
err = div_u64_rem(start, mmc->erase_grp_size, &start_rem); err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem); err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
if (start_rem || blkcnt_rem) if (start_rem || blkcnt_rem) {
printf("\n\nCaution! Your devices Erase group is 0x%x\n" if (mmc->can_trim) {
"The erase range would be change to " /* Trim function applies the erase operation to write
"0x" LBAF "~0x" LBAF "\n\n", * blocks instead of erase groups.
mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), */
((start + blkcnt + mmc->erase_grp_size - 1) erase_args = MMC_TRIM_ARG;
& ~(mmc->erase_grp_size - 1)) - 1); } else {
/* The card ignores all LSB's below the erase group
* size, rounding down the addess to a erase group
* boundary.
*/
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 - 1)
& ~(mmc->erase_grp_size - 1)) - 1);
}
}
while (blk < blkcnt) { while (blk < blkcnt) {
if (IS_SD(mmc) && mmc->ssr.au) { if (IS_SD(mmc) && mmc->ssr.au) {
@ -113,7 +125,7 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt)
blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
mmc->erase_grp_size : (blkcnt - blk); mmc->erase_grp_size : (blkcnt - blk);
} }
err = mmc_erase_t(mmc, start + blk, blk_r); err = mmc_erase_t(mmc, start + blk, blk_r, erase_args);
if (err) if (err)
break; break;