diff options
author | Lei Wen <leiwen@marvell.com> | 2011-06-22 17:03:31 +0000 |
---|---|---|
committer | Andy Fleming <afleming@freescale.com> | 2011-07-15 20:29:17 -0500 |
commit | e6f99a5611e1ff59555f93de88e527070f8548af (patch) | |
tree | 77577e3a0f6db5f84f2d9d879e9abfc221685c69 | |
parent | 6be95ccf9ff52d629526d1c20c5343c2a91d9e72 (diff) |
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>
Signed-off-by: Andy Fleming <afleming@freescale.com>
Acked-by: Mike Frysinger <vapier@gentoo.org>
-rw-r--r-- | common/cmd_mmc.c | 22 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 102 | ||||
-rw-r--r-- | include/mmc.h | 8 | ||||
-rw-r--r-- | include/part.h | 3 |
4 files changed, 131 insertions, 4 deletions
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index a6458037e22..7335cdc7577 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; + int idx = 2; + u32 blk, cnt, n; + void *addr; + + if (state != MMC_ERASE) { + addr = (void *)simple_strtoul(argv[idx], NULL, 16); + ++idx; + } else + addr = 0; + blk = simple_strtoul(argv[idx], NULL, 16); + cnt = simple_strtoul(argv[idx + 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 21aedbaa3fe..9a1ee3d3984 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%lx~0x%lx\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 aeacdee3095..1c8a3607177 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 3cdae0214ee..52435118290 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; |