[U-Boot] [PATCH] MMC: add erase function to both mmc and sd

Erase is a very basic function since the begin of sd specification is announced. Although we could write a bulk of full 0xff memory to the range to take place of erase, it is more convenient and safe to implement the erase function itself.
Signed-off-by: Lei Wen leiwen@marvell.com --- common/cmd_mmc.c | 23 ++++++++++++ drivers/mmc/mmc.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 ++++ include/part.h | 3 ++ 4 files changed, 136 insertions(+), 0 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 176646d..a6483ba 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -287,6 +287,28 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); return (n == cnt) ? 0 : 1; + } else if (strcmp(argv[1], "erase") == 0) { + u32 cnt = simple_strtoul(argv[3], NULL, 16); + u32 n; + struct mmc *mmc = find_mmc_device(dev); + + int blk = simple_strtoul(argv[2], NULL, 16); + + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } + + printf("\nMMC erase: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + + mmc_init(mmc); + + n = mmc->block_dev.block_erase(curr_device, blk, cnt); + + printf("%d blocks erased: %s\n", + n, (n == cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; }
return cmd_usage(cmdtp); @@ -297,6 +319,7 @@ U_BOOT_CMD( "MMC sub system", "read addr blk# cnt\n" "mmc write addr blk# cnt\n" + "mmc erase addr blk# cnt\n" "mmc rescan\n" "mmc part - lists available partition on current mmc device\n" "mmc dev [dev] [part] - show or set current mmc device [partition]\n" diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 21aedba..64bb2a8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -174,6 +174,88 @@ struct mmc *find_mmc_device(int dev_num) return NULL; }
+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; + cmd.flags = 0; + + 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: + printf("mmc erase failed\n\r"); + return err; +} + +static unsigned long +mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + + 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%x~0x%x\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; + } + + return blk; +} + static ulong mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) { @@ -911,6 +993,10 @@ int mmc_startup(struct mmc *mmc) return err; }
+ /* + * For SD, its erase group is always one sector + */ + mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ @@ -921,6 +1007,21 @@ int mmc_startup(struct mmc *mmc) mmc->capacity *= 512; }
+ /* + * Check whether GROUP_DEF is set, if yes, read out + * group size from ext_csd directly, or calculate + * the group size from the csd value. + */ + if (ext_csd[175]) + mmc->erase_grp_size = ext_csd[224] * 512 * 1024; + else { + int erase_gsz, erase_gmul; + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } + /* store the partition info of emmc */ if (ext_csd[160] & PART_SUPPORT) mmc->part_config = ext_csd[179]; @@ -1044,6 +1145,7 @@ int mmc_register(struct mmc *mmc) mmc->block_dev.removable = 1; mmc->block_dev.block_read = mmc_bread; mmc->block_dev.block_write = mmc_bwrite; + mmc->block_dev.block_erase = mmc_berase; if (!mmc->b_max) mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
diff --git a/include/mmc.h b/include/mmc.h index aeacdee..1c8a360 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -75,6 +75,9 @@ #define MMC_CMD_READ_MULTIPLE_BLOCK 18 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 +#define MMC_CMD_ERASE_GROUP_START 35 +#define MMC_CMD_ERASE_GROUP_END 36 +#define MMC_CMD_ERASE 38 #define MMC_CMD_APP_CMD 55 #define MMC_CMD_SPI_READ_OCR 58 #define MMC_CMD_SPI_CRC_ON_OFF 59 @@ -84,6 +87,8 @@ #define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SET_BUS_WIDTH 6 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 #define SD_CMD_APP_SEND_OP_COND 41 #define SD_CMD_APP_SEND_SCR 51
@@ -99,6 +104,8 @@ #define OCR_VOLTAGE_MASK 0x007FFF80 #define OCR_ACCESS_MODE 0x60000000
+#define SECURE_ERASE 0x80000000 + #define MMC_STATUS_MASK (~0x0206BF7F) #define MMC_STATUS_RDY_FOR_DATA (1 << 8) #define MMC_STATUS_CURR_STATE (0xf << 9) @@ -285,6 +292,7 @@ struct mmc { uint tran_speed; uint read_bl_len; uint write_bl_len; + uint erase_grp_size; u64 capacity; block_dev_desc_t block_dev; int (*send_cmd)(struct mmc *mmc, diff --git a/include/part.h b/include/part.h index 3cdae02..5243511 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,9 @@ typedef struct block_dev_desc { unsigned long start, lbaint_t blkcnt, const void *buffer); + unsigned long (*block_erase)(int dev, + unsigned long start, + lbaint_t blkcnt); void *priv; /* driver private struct pointer */ }block_dev_desc_t;

On Thursday, June 16, 2011 09:47:56 Lei Wen wrote:
- } else if (strcmp(argv[1], "erase") == 0) {
u32 cnt = simple_strtoul(argv[3], NULL, 16);
u32 n;
struct mmc *mmc = find_mmc_device(dev);
int blk = simple_strtoul(argv[2], NULL, 16);
if (!mmc) {
printf("no mmc device at slot %x\n", curr_device);
return 1;
}
printf("\nMMC erase: dev # %d, block # %d, count %d ... ",
curr_device, blk, cnt);
mmc_init(mmc);
this logic really needs to get cleaned up rather than every sub-mmc command doing the same thing over and over ...
- printf("mmc erase failed\n\r");
no output strings should include \r in them -mike

Hi Mike,
On Fri, Jun 17, 2011 at 8:57 AM, Mike Frysinger vapier@gentoo.org wrote:
On Thursday, June 16, 2011 09:47:56 Lei Wen wrote:
- } else if (strcmp(argv[1], "erase") == 0) {
- u32 cnt = simple_strtoul(argv[3], NULL, 16);
- u32 n;
- struct mmc *mmc = find_mmc_device(dev);
- int blk = simple_strtoul(argv[2], NULL, 16);
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
- printf("\nMMC erase: dev # %d, block # %d, count %d ... ",
- curr_device, blk, cnt);
- mmc_init(mmc);
this logic really needs to get cleaned up rather than every sub-mmc command doing the same thing over and over ...
Agree, I would sort it out as a seperate patch.
- printf("mmc erase failed\n\r");
no output strings should include \r in them
Opps... I would correct that.
Thanks, Lei

On Thursday, June 16, 2011 23:09:40 Lei Wen wrote:
On Fri, Jun 17, 2011 at 8:57 AM, Mike Frysinger wrote:
this logic really needs to get cleaned up rather than every sub-mmc command doing the same thing over and over ...
Agree, I would sort it out as a seperate patch.
thanks. this further will implicitly force consistency across the sub- commands. -mike

Changelog: V2: unfiy original mmc read and write command together to only place Based on the unfied result, also add the erase command into that place.
Lei Wen (2): MMC: unify mmc read and write operation MMC: add erase function to both mmc and sd
common/cmd_mmc.c | 84 +++++++++++++++++++++++++------------------ drivers/mmc/mmc.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 ++++ include/part.h | 3 ++ 4 files changed, 162 insertions(+), 35 deletions(-)

Changelog: V2: unfiy original mmc read and write command together to only place Based on the unfied result, also add the erase command into that place.
V3: put varible statement at the beginning of function. convert printf to puts for only print out string. Further unify getting blk and cnt method.
Lei Wen (2): MMC: unify mmc read and write operation MMC: add erase function to both mmc and sd
common/cmd_mmc.c | 83 ++++++++++++++++++++++++++----------------- drivers/mmc/mmc.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 ++++ include/part.h | 3 ++ 4 files changed, 163 insertions(+), 33 deletions(-)

mmc read and write command has so many in common, unfiy those two to force consistency across the those two.
Signed-off-by: Lei Wen leiwen@marvell.com --- Changelog: V2: unfiy original mmc read and write command together to only place
V3: put varible statement at the beginning of function.
common/cmd_mmc.c | 67 ++++++++++++++++++++++++++++------------------------- 1 files changed, 35 insertions(+), 32 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 176646d..a645803 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -87,6 +87,11 @@ U_BOOT_CMD( ); #else /* !CONFIG_GENERIC_MMC */
+enum mmc_state { + MMC_INVALID, + MMC_READ, + MMC_WRITE, +}; static void print_mmcinfo(struct mmc *mmc) { printf("Device: %s\n", mmc->name); @@ -144,6 +149,8 @@ U_BOOT_CMD(
int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { + enum mmc_state state; + if (argc < 2) return cmd_usage(cmdtp);
@@ -239,53 +246,49 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) curr_device, mmc->part_num);
return 0; - } else if (strcmp(argv[1], "read") == 0) { - void *addr = (void *)simple_strtoul(argv[2], NULL, 16); - u32 cnt = simple_strtoul(argv[4], NULL, 16); - u32 n; - u32 blk = simple_strtoul(argv[3], NULL, 16); - struct mmc *mmc = find_mmc_device(curr_device); - - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } - - printf("\nMMC read: dev # %d, block # %d, count %d ... ", - curr_device, blk, cnt); - - mmc_init(mmc); - - n = mmc->block_dev.block_read(curr_device, blk, cnt, addr); + }
- /* flush cache after read */ - flush_cache((ulong)addr, cnt * 512); /* FIXME */ + if (strcmp(argv[1], "read") == 0) + state = MMC_READ; + else if (strcmp(argv[1], "write") == 0) + state = MMC_WRITE; + else + state = MMC_INVALID;
- printf("%d blocks read: %s\n", - n, (n==cnt) ? "OK" : "ERROR"); - return (n == cnt) ? 0 : 1; - } else if (strcmp(argv[1], "write") == 0) { + if (state != MMC_INVALID) { + struct mmc *mmc = find_mmc_device(curr_device); void *addr = (void *)simple_strtoul(argv[2], NULL, 16); + u32 blk = simple_strtoul(argv[3], NULL, 16); u32 cnt = simple_strtoul(argv[4], NULL, 16); u32 n; - struct mmc *mmc = find_mmc_device(curr_device); - - int blk = simple_strtoul(argv[3], NULL, 16);
if (!mmc) { printf("no mmc device at slot %x\n", curr_device); return 1; }
- printf("\nMMC write: dev # %d, block # %d, count %d ... ", - curr_device, blk, cnt); + printf("\nMMC %s: dev # %d, block # %d, count %d ... ", + argv[1], curr_device, blk, cnt);
mmc_init(mmc);
- n = mmc->block_dev.block_write(curr_device, blk, cnt, addr); + switch (state) { + case MMC_READ: + n = mmc->block_dev.block_read(curr_device, blk, + cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); /* FIXME */ + break; + case MMC_WRITE: + n = mmc->block_dev.block_write(curr_device, blk, + cnt, addr); + break; + default: + BUG(); + }
- printf("%d blocks written: %s\n", - n, (n == cnt) ? "OK" : "ERROR"); + printf("%d blocks %s: %s\n", + n, argv[1], (n == cnt) ? "OK" : "ERROR"); return (n == cnt) ? 0 : 1; }

On Wed, Jun 22, 2011 at 8:03 PM, Lei Wen leiwen@marvell.com wrote:
mmc read and write command has so many in common, unfiy those two to force consistency across the those two.
Signed-off-by: Lei Wen leiwen@marvell.com
Changelog: V2: unfiy original mmc read and write command together to only place
V3: put varible statement at the beginning of function.
common/cmd_mmc.c | 67 ++++++++++++++++++++++++++++------------------------- 1 files changed, 35 insertions(+), 32 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 176646d..a645803 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -87,6 +87,11 @@ U_BOOT_CMD( ); #else /* !CONFIG_GENERIC_MMC */
+enum mmc_state {
MMC_INVALID,
MMC_READ,
MMC_WRITE,
+}; static void print_mmcinfo(struct mmc *mmc) { printf("Device: %s\n", mmc->name); @@ -144,6 +149,8 @@ U_BOOT_CMD(
int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
enum mmc_state state;
if (argc < 2) return cmd_usage(cmdtp);
@@ -239,53 +246,49 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) curr_device, mmc->part_num);
return 0;
} else if (strcmp(argv[1], "read") == 0) {
void *addr = (void *)simple_strtoul(argv[2], NULL, 16);
u32 cnt = simple_strtoul(argv[4], NULL, 16);
u32 n;
u32 blk = simple_strtoul(argv[3], NULL, 16);
struct mmc *mmc = find_mmc_device(curr_device);
if (!mmc) {
printf("no mmc device at slot %x\n", curr_device);
return 1;
}
printf("\nMMC read: dev # %d, block # %d, count %d ... ",
curr_device, blk, cnt);
mmc_init(mmc);
n = mmc->block_dev.block_read(curr_device, blk, cnt, addr);
}
/* flush cache after read */
flush_cache((ulong)addr, cnt * 512); /* FIXME */
if (strcmp(argv[1], "read") == 0)
state = MMC_READ;
else if (strcmp(argv[1], "write") == 0)
state = MMC_WRITE;
else
state = MMC_INVALID;
printf("%d blocks read: %s\n",
n, (n==cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else if (strcmp(argv[1], "write") == 0) {
if (state != MMC_INVALID) {
struct mmc *mmc = find_mmc_device(curr_device); void *addr = (void *)simple_strtoul(argv[2], NULL, 16);
u32 blk = simple_strtoul(argv[3], NULL, 16); u32 cnt = simple_strtoul(argv[4], NULL, 16); u32 n;
struct mmc *mmc = find_mmc_device(curr_device);
int blk = simple_strtoul(argv[3], NULL, 16); if (!mmc) { printf("no mmc device at slot %x\n", curr_device); return 1; }
printf("\nMMC write: dev # %d, block # %d, count %d ... ",
curr_device, blk, cnt);
printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
argv[1], curr_device, blk, cnt); mmc_init(mmc);
n = mmc->block_dev.block_write(curr_device, blk, cnt,
addr);
switch (state) {
case MMC_READ:
n = mmc->block_dev.block_read(curr_device, blk,
cnt, addr);
/* flush cache after read */
flush_cache((ulong)addr, cnt * 512); /* FIXME */
break;
case MMC_WRITE:
n = mmc->block_dev.block_write(curr_device, blk,
cnt, addr);
break;
default:
BUG();
}
printf("%d blocks written: %s\n",
n, (n == cnt) ? "OK" : "ERROR");
printf("%d blocks %s: %s\n",
n, argv[1], (n == cnt) ? "OK" : "ERROR");
A minor nit, this might read more smoothly if it were: printf("%s of %d blocks: %s\n", argv[1], n, (n == cnt) ? "OK" : "ERROR"); As in "read of 12 blocks: OK" instead of "12 blocks read: OK"
return (n == cnt) ? 0 : 1;
}
-- 1.7.0.4
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Acked-by: Mike Frysinger vapier@gentoo.org -mike

Erase is a very basic function since the begin of sd specification is announced. Although we could write a bulk of full 0xff memory to the range to take place of erase, it is more convenient and safe to implement the erase function itself.
Signed-off-by: Lei Wen leiwen@marvell.com --- Changelog: V2: Based on the unfied result, also add the erase command into that place
V3: convert printf to puts for only print out string. Further unify getting blk and cnt method.
common/cmd_mmc.c | 22 +++++++++-- drivers/mmc/mmc.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 ++++ include/part.h | 3 ++ 4 files changed, 131 insertions(+), 4 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index a645803..28d3635 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -91,6 +91,7 @@ enum mmc_state { MMC_INVALID, MMC_READ, MMC_WRITE, + MMC_ERASE, }; static void print_mmcinfo(struct mmc *mmc) { @@ -252,15 +253,24 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) state = MMC_READ; else if (strcmp(argv[1], "write") == 0) state = MMC_WRITE; + else if (strcmp(argv[1], "erase") == 0) + state = MMC_ERASE; else state = MMC_INVALID;
if (state != MMC_INVALID) { struct mmc *mmc = find_mmc_device(curr_device); - void *addr = (void *)simple_strtoul(argv[2], NULL, 16); - u32 blk = simple_strtoul(argv[3], NULL, 16); - u32 cnt = simple_strtoul(argv[4], NULL, 16); - u32 n; + char **args = &argv[2]; + u32 blk, cnt, n; + void *addr; + + if (state != MMC_ERASE) { + addr = (void *)simple_strtoul(args[0], NULL, 16); + ++args; + } else + addr = 0; + blk = simple_strtoul(args[0], NULL, 16); + cnt = simple_strtoul(args[1], NULL, 16);
if (!mmc) { printf("no mmc device at slot %x\n", curr_device); @@ -283,6 +293,9 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) n = mmc->block_dev.block_write(curr_device, blk, cnt, addr); break; + case MMC_ERASE: + n = mmc->block_dev.block_erase(curr_device, blk, cnt); + break; default: BUG(); } @@ -300,6 +313,7 @@ U_BOOT_CMD( "MMC sub system", "read addr blk# cnt\n" "mmc write addr blk# cnt\n" + "mmc erase blk# cnt\n" "mmc rescan\n" "mmc part - lists available partition on current mmc device\n" "mmc dev [dev] [part] - show or set current mmc device [partition]\n" diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 21aedba..df5f0a6 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -174,6 +174,88 @@ struct mmc *find_mmc_device(int dev_num) return NULL; }
+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; + cmd.flags = 0; + + 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; +} + +static unsigned long +mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + + 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%x~0x%x\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; + } + + return blk; +} + static ulong mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) { @@ -911,6 +993,10 @@ int mmc_startup(struct mmc *mmc) return err; }
+ /* + * For SD, its erase group is always one sector + */ + mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ @@ -921,6 +1007,21 @@ int mmc_startup(struct mmc *mmc) mmc->capacity *= 512; }
+ /* + * Check whether GROUP_DEF is set, if yes, read out + * group size from ext_csd directly, or calculate + * the group size from the csd value. + */ + if (ext_csd[175]) + mmc->erase_grp_size = ext_csd[224] * 512 * 1024; + else { + int erase_gsz, erase_gmul; + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } + /* store the partition info of emmc */ if (ext_csd[160] & PART_SUPPORT) mmc->part_config = ext_csd[179]; @@ -1044,6 +1145,7 @@ int mmc_register(struct mmc *mmc) mmc->block_dev.removable = 1; mmc->block_dev.block_read = mmc_bread; mmc->block_dev.block_write = mmc_bwrite; + mmc->block_dev.block_erase = mmc_berase; if (!mmc->b_max) mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
diff --git a/include/mmc.h b/include/mmc.h index aeacdee..1c8a360 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -75,6 +75,9 @@ #define MMC_CMD_READ_MULTIPLE_BLOCK 18 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 +#define MMC_CMD_ERASE_GROUP_START 35 +#define MMC_CMD_ERASE_GROUP_END 36 +#define MMC_CMD_ERASE 38 #define MMC_CMD_APP_CMD 55 #define MMC_CMD_SPI_READ_OCR 58 #define MMC_CMD_SPI_CRC_ON_OFF 59 @@ -84,6 +87,8 @@ #define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SET_BUS_WIDTH 6 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 #define SD_CMD_APP_SEND_OP_COND 41 #define SD_CMD_APP_SEND_SCR 51
@@ -99,6 +104,8 @@ #define OCR_VOLTAGE_MASK 0x007FFF80 #define OCR_ACCESS_MODE 0x60000000
+#define SECURE_ERASE 0x80000000 + #define MMC_STATUS_MASK (~0x0206BF7F) #define MMC_STATUS_RDY_FOR_DATA (1 << 8) #define MMC_STATUS_CURR_STATE (0xf << 9) @@ -285,6 +292,7 @@ struct mmc { uint tran_speed; uint read_bl_len; uint write_bl_len; + uint erase_grp_size; u64 capacity; block_dev_desc_t block_dev; int (*send_cmd)(struct mmc *mmc, diff --git a/include/part.h b/include/part.h index 3cdae02..5243511 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,9 @@ typedef struct block_dev_desc { unsigned long start, lbaint_t blkcnt, const void *buffer); + unsigned long (*block_erase)(int dev, + unsigned long start, + lbaint_t blkcnt); void *priv; /* driver private struct pointer */ }block_dev_desc_t;

Acked-by: Mike Frysinger vapier@gentoo.org -mike

mmc read and write command has so many in common, unfiy those two to force consistency across the those two.
Signed-off-by: Lei Wen leiwen@marvell.com --- Changelog: V2: unfiy original mmc read and write command together to only place
common/cmd_mmc.c | 67 +++++++++++++++++++++++++++-------------------------- 1 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 176646d..2f24578 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -87,6 +87,11 @@ U_BOOT_CMD( ); #else /* !CONFIG_GENERIC_MMC */
+enum mmc_state { + MMC_INVALID, + MMC_READ, + MMC_WRITE, +}; static void print_mmcinfo(struct mmc *mmc) { printf("Device: %s\n", mmc->name); @@ -239,53 +244,49 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) curr_device, mmc->part_num);
return 0; - } else if (strcmp(argv[1], "read") == 0) { - void *addr = (void *)simple_strtoul(argv[2], NULL, 16); - u32 cnt = simple_strtoul(argv[4], NULL, 16); - u32 n; - u32 blk = simple_strtoul(argv[3], NULL, 16); - struct mmc *mmc = find_mmc_device(curr_device); - - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } - - printf("\nMMC read: dev # %d, block # %d, count %d ... ", - curr_device, blk, cnt); - - mmc_init(mmc); - - n = mmc->block_dev.block_read(curr_device, blk, cnt, addr); - - /* flush cache after read */ - flush_cache((ulong)addr, cnt * 512); /* FIXME */ + }
- printf("%d blocks read: %s\n", - n, (n==cnt) ? "OK" : "ERROR"); - return (n == cnt) ? 0 : 1; - } else if (strcmp(argv[1], "write") == 0) { + enum mmc_state state; + if (strcmp(argv[1], "read") == 0) + state = MMC_READ; + else if (strcmp(argv[1], "write") == 0) + state = MMC_WRITE; + else + state = MMC_INVALID; + if (state != MMC_INVALID) { + struct mmc *mmc = find_mmc_device(curr_device); void *addr = (void *)simple_strtoul(argv[2], NULL, 16); + u32 blk = simple_strtoul(argv[3], NULL, 16); u32 cnt = simple_strtoul(argv[4], NULL, 16); u32 n; - struct mmc *mmc = find_mmc_device(curr_device); - - int blk = simple_strtoul(argv[3], NULL, 16);
if (!mmc) { printf("no mmc device at slot %x\n", curr_device); return 1; }
- printf("\nMMC write: dev # %d, block # %d, count %d ... ", - curr_device, blk, cnt); + printf("\nMMC %s: dev # %d, block # %d, count %d ... ", + argv[1], curr_device, blk, cnt);
mmc_init(mmc);
- n = mmc->block_dev.block_write(curr_device, blk, cnt, addr); + switch (state) { + case MMC_READ: + n = mmc->block_dev.block_read(curr_device, blk, + cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); /* FIXME */ + break; + case MMC_WRITE: + n = mmc->block_dev.block_write(curr_device, blk, + cnt, addr); + break; + default: + BUG(); + }
- printf("%d blocks written: %s\n", - n, (n == cnt) ? "OK" : "ERROR"); + printf("%d blocks %s: %s\n", + n, argv[1], (n == cnt) ? "OK" : "ERROR"); return (n == cnt) ? 0 : 1; }

On Monday, June 20, 2011 10:10:15 Lei Wen wrote:
- enum mmc_state state;
- if (strcmp(argv[1], "read") == 0)
state = MMC_READ;
- else if (strcmp(argv[1], "write") == 0)
state = MMC_WRITE;
- else
state = MMC_INVALID;
i think wolfgang hates inline var decls (c99), so that "state" will have to be moved up. also, there should be a newline after this if block. -mike

Erase is a very basic function since the begin of sd specification is announced. Although we could write a bulk of full 0xff memory to the range to take place of erase, it is more convenient and safe to implement the erase function itself.
Signed-off-by: Lei Wen leiwen@marvell.com --- Changelog: V2: Based on the unfied result, also add the erase command into that place
common/cmd_mmc.c | 23 +++++++++--- drivers/mmc/mmc.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 ++++ include/part.h | 3 ++ 4 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 2f24578..7af7678 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -91,6 +91,7 @@ enum mmc_state { MMC_INVALID, MMC_READ, MMC_WRITE, + MMC_ERASE, }; static void print_mmcinfo(struct mmc *mmc) { @@ -251,25 +252,33 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) state = MMC_READ; else if (strcmp(argv[1], "write") == 0) state = MMC_WRITE; + else if (strcmp(argv[1], "erase") == 0) + state = MMC_ERASE; else state = MMC_INVALID; if (state != MMC_INVALID) { struct mmc *mmc = find_mmc_device(curr_device); - void *addr = (void *)simple_strtoul(argv[2], NULL, 16); - u32 blk = simple_strtoul(argv[3], NULL, 16); - u32 cnt = simple_strtoul(argv[4], NULL, 16); - u32 n; + void *addr = 0; + u32 blk, cnt, n;
if (!mmc) { printf("no mmc device at slot %x\n", curr_device); return 1; }
+ if (state == MMC_ERASE) { + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + } else { + addr = (void *)simple_strtoul(argv[2], NULL, 16); + blk = simple_strtoul(argv[3], NULL, 16); + cnt = simple_strtoul(argv[4], NULL, 16); + } + printf("\nMMC %s: dev # %d, block # %d, count %d ... ", argv[1], curr_device, blk, cnt);
mmc_init(mmc); - switch (state) { case MMC_READ: n = mmc->block_dev.block_read(curr_device, blk, @@ -281,6 +290,9 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) n = mmc->block_dev.block_write(curr_device, blk, cnt, addr); break; + case MMC_ERASE: + n = mmc->block_dev.block_erase(curr_device, blk, cnt); + break; default: BUG(); } @@ -298,6 +310,7 @@ U_BOOT_CMD( "MMC sub system", "read addr blk# cnt\n" "mmc write addr blk# cnt\n" + "mmc erase addr blk# cnt\n" "mmc rescan\n" "mmc part - lists available partition on current mmc device\n" "mmc dev [dev] [part] - show or set current mmc device [partition]\n" diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 21aedba..64bb2a8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -174,6 +174,88 @@ struct mmc *find_mmc_device(int dev_num) return NULL; }
+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; + cmd.flags = 0; + + 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: + printf("mmc erase failed\n\r"); + return err; +} + +static unsigned long +mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + + 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%x~0x%x\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; + } + + return blk; +} + static ulong mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) { @@ -911,6 +993,10 @@ int mmc_startup(struct mmc *mmc) return err; }
+ /* + * For SD, its erase group is always one sector + */ + mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ @@ -921,6 +1007,21 @@ int mmc_startup(struct mmc *mmc) mmc->capacity *= 512; }
+ /* + * Check whether GROUP_DEF is set, if yes, read out + * group size from ext_csd directly, or calculate + * the group size from the csd value. + */ + if (ext_csd[175]) + mmc->erase_grp_size = ext_csd[224] * 512 * 1024; + else { + int erase_gsz, erase_gmul; + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } + /* store the partition info of emmc */ if (ext_csd[160] & PART_SUPPORT) mmc->part_config = ext_csd[179]; @@ -1044,6 +1145,7 @@ int mmc_register(struct mmc *mmc) mmc->block_dev.removable = 1; mmc->block_dev.block_read = mmc_bread; mmc->block_dev.block_write = mmc_bwrite; + mmc->block_dev.block_erase = mmc_berase; if (!mmc->b_max) mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
diff --git a/include/mmc.h b/include/mmc.h index aeacdee..1c8a360 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -75,6 +75,9 @@ #define MMC_CMD_READ_MULTIPLE_BLOCK 18 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 +#define MMC_CMD_ERASE_GROUP_START 35 +#define MMC_CMD_ERASE_GROUP_END 36 +#define MMC_CMD_ERASE 38 #define MMC_CMD_APP_CMD 55 #define MMC_CMD_SPI_READ_OCR 58 #define MMC_CMD_SPI_CRC_ON_OFF 59 @@ -84,6 +87,8 @@ #define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SET_BUS_WIDTH 6 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 #define SD_CMD_APP_SEND_OP_COND 41 #define SD_CMD_APP_SEND_SCR 51
@@ -99,6 +104,8 @@ #define OCR_VOLTAGE_MASK 0x007FFF80 #define OCR_ACCESS_MODE 0x60000000
+#define SECURE_ERASE 0x80000000 + #define MMC_STATUS_MASK (~0x0206BF7F) #define MMC_STATUS_RDY_FOR_DATA (1 << 8) #define MMC_STATUS_CURR_STATE (0xf << 9) @@ -285,6 +292,7 @@ struct mmc { uint tran_speed; uint read_bl_len; uint write_bl_len; + uint erase_grp_size; u64 capacity; block_dev_desc_t block_dev; int (*send_cmd)(struct mmc *mmc, diff --git a/include/part.h b/include/part.h index 3cdae02..5243511 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,9 @@ typedef struct block_dev_desc { unsigned long start, lbaint_t blkcnt, const void *buffer); + unsigned long (*block_erase)(int dev, + unsigned long start, + lbaint_t blkcnt); void *priv; /* driver private struct pointer */ }block_dev_desc_t;

On Monday, June 20, 2011 10:10:16 Lei Wen wrote:
if (state == MMC_ERASE) {
blk = simple_strtoul(argv[2], NULL, 16);
cnt = simple_strtoul(argv[3], NULL, 16);
} else {
addr = (void *)simple_strtoul(argv[2], NULL, 16);
blk = simple_strtoul(argv[3], NULL, 16);
cnt = simple_strtoul(argv[4], NULL, 16);
}
you could do: char **args = &argv[2]; if (state != MMC_ERASE) { addr = (void *)simple_strtoul(args[0], NULL, 16); ++args; } else addr = 0; blk = simple_strtoul(args[0], NULL, 16); simple_strtoul(args[1], NULL, 16);
- "mmc erase addr blk# cnt\n"
i dont think the "erase" cmd takes an addr
+err_out:
- printf("mmc erase failed\n\r");
dont use \r in strings, and use puts() -mike

Hi Mike,
On Thu, Jun 23, 2011 at 12:57 AM, Mike Frysinger vapier@gentoo.org wrote:
On Monday, June 20, 2011 10:10:16 Lei Wen wrote:
- if (state == MMC_ERASE) {
- blk = simple_strtoul(argv[2], NULL, 16);
- cnt = simple_strtoul(argv[3], NULL, 16);
- } else {
- addr = (void *)simple_strtoul(argv[2], NULL, 16);
- blk = simple_strtoul(argv[3], NULL, 16);
- cnt = simple_strtoul(argv[4], NULL, 16);
- }
you could do: char **args = &argv[2]; if (state != MMC_ERASE) { addr = (void *)simple_strtoul(args[0], NULL, 16); ++args; } else addr = 0; blk = simple_strtoul(args[0], NULL, 16); simple_strtoul(args[1], NULL, 16);
- "mmc erase addr blk# cnt\n"
i dont think the "erase" cmd takes an addr
+err_out:
- printf("mmc erase failed\n\r");
dont use \r in strings, and use puts()
Thanks for comments, new version pushed with your suggestion involved.
Best regards, Lei
participants (4)
-
Anton Staaf
-
Lei Wen
-
Lei Wen
-
Mike Frysinger