diff options
author | Scott Anderson <saa@google.com> | 2011-09-29 18:44:37 -0700 |
---|---|---|
committer | Mike J. Chen <mjchen@google.com> | 2012-03-03 17:29:07 -0800 |
commit | 0927afab8d90d69e7503918904b5290f3e593c17 (patch) | |
tree | 881089a102e385433b05f386d34d6fd87228b424 /drivers/mmc | |
parent | 739be4d47805440ed2ab72fe014f289edc77f925 (diff) | |
download | uboot-0927afab8d90d69e7503918904b5290f3e593c17.tar.gz |
MMC: Enhance mmc_berase() to handle blks < erase block size
MMC defines the erase block size in terms of a number of blocks.
Formerly berase() rounded what would be erased and printed a
warning message. This change enhances it to write ones to any
blocks that would not be a part of an entire erase block.
Signed-off-by: Scott Anderson <saa@google.com>
Change-Id: I4903f7db3078d4e27a3c1746b5a12a306d5d71df
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/mmc.c | 125 |
1 files changed, 108 insertions, 17 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0f16a3f4d..fccde1f83 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -40,6 +40,10 @@ static struct list_head mmc_devices; static int cur_dev_num = -1; +/* Prototype to avoid code shuffle. */ +static ulong mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, + const void *src); + int __board_mmc_getcd(u8 *cd, struct mmc *mmc) { return -1; } @@ -227,36 +231,123 @@ err_out: return err; } +static ulong mmc_writeFF(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt) +{ +#ifdef CONFIG_SPL_BUILD + /* + * SPL doesn't have enough memory for the all_ones buffer, nor + * does it need it. + */ + return UNUSABLE_ERR; +#else + static void *all_ones; + static uint all_ones_len; + int err = 0; + + /* + * We need a buffer filled with all ones that needs to be able to + * write block sized chunks repeatedly up to mmc->erase_grp_size + * times. It will be large, so use the heap instead of stack. I keep + * a pointer to what is allocated in a static pointer instead of doing + * malloc/memset/free on every call to this function. I assume there + * is a (small) chance that different MMCs will have different block + * sizes, so that case is handled. + */ + uint blk_len = mmc->write_bl_len; + + if (all_ones_len < blk_len) { + if (all_ones_len) + free(all_ones); + all_ones = malloc(blk_len); + if (!all_ones) { + all_ones_len = 0; + err = -1; + puts("mmc write ones failed to get memory\n"); + goto exit; + } + all_ones_len = blk_len; + memset(all_ones, 0xFF, all_ones_len); + } + + if (mmc_set_blocklen(mmc, blk_len)) { + puts("mmc write ones failed to set write length\n"); + goto exit; + } + while (blkcnt--) { + if (mmc_write_blocks(mmc, start++, 1, all_ones) != 1) { + err = UNUSABLE_ERR; + puts("mmc write ones failed to write\n"); + goto exit; + } + } + +exit: +#ifdef CONFIG_MMC_FREE_ALL_ONES + /* Option to the memory freed just in case someone wants it. */ + free(all_ones); + all_ones_len = 0; +#endif + return err; +#endif /* ! CONFIG_SPL_BUILD */ +} + static unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) { int err = 0; struct mmc *mmc = find_mmc_device(dev_num); - lbaint_t blk = 0, blk_r = 0; + lbaint_t next = start, blks_left = blkcnt, blk_r; 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); - printf("mmc_erase_t(), start %lu, blk_cnt %lu, returned %d\n", - start+blk, blk_r, err); + /* + * Figure out how many blocks are before an erase_grp_size + * boundary. We do that by figuring out where the next + * erase_grp_size boundary is and then subtracting start. + */ + blk_r = roundup(start, mmc->erase_grp_size) - start; + if (blk_r > blkcnt) + blk_r = blkcnt; + if (blk_r) { + err = mmc_writeFF(mmc, next, blk_r); + printf("mmc_writeFF(), start %lu, blk_cnt %lu, returned %d\n", + next, blk_r, err); if (err) - break; + goto exit; + blks_left -= blk_r; + next += blk_r; + } - blk += blk_r; + /* + * Now loop erasing erase_grp_size blocks each time. At first I tried + * to do an erase for the whole thing at once, but timeouts ensued, so + * I went back to the former approach. + */ + while (blks_left > mmc->erase_grp_size) { + err = mmc_erase_t(mmc, next, mmc->erase_grp_size); + printf("mmc_erase_t(), start %lu, blk_cnt %u, returned %d\n", + next, mmc->erase_grp_size, err); + if (err) + goto exit; + blks_left -= mmc->erase_grp_size; + next += mmc->erase_grp_size; } - return blk; + /* + * Take care of any blocks after the erase_grp_size boundary. + */ + if (blks_left) { + /* Area to erase does not end on an erase group boundary. */ + err = mmc_writeFF(mmc, next, blks_left); + printf("mmc_writeFF(), start %lu, blk_cnt %lu, returned %d\n", + next, blks_left, err); + if (err) + goto exit; + blks_left = 0; + } +exit: + return blkcnt - blks_left; /* Return number of blocks erased. */ } static ulong |