
Added Multi-Block Read support for Generic MMC. Modified existing multi-block write to limit the maximum number of blocks per transfer. This feature is enabled with CONFIG_MMC_MBLOCK option. A new member is added in the mmc structure for the host controller to specify the maximum number of blocks it supports.
Signed-off-by: Alagu Sankar alagusankar@embwise.com --- drivers/mmc/mmc.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 3 + 2 files changed, 159 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e7abf94..3f5a200 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -77,6 +77,7 @@ struct mmc *find_mmc_device(int dev_num) return NULL; }
+#ifndef CONFIG_MMC_MBLOCK static ulong mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) { @@ -238,6 +239,156 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return blkcnt; }
+#else + +static int mmc_write_blocks(struct mmc *mmc, const char *src, uint start, + uint blkcnt) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + int blklen; + + blklen = mmc->write_bl_len; + + err = mmc_set_blocklen(mmc, mmc->write_bl_len); + + if (err) { + printf("set write bl len failed\n\r"); + return err; + } + + if (blkcnt > 1) + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * blklen; + + cmd.resp_type = MMC_RSP_R1; + cmd.flags = 0; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = blklen; + data.flags = MMC_DATA_WRITE; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + printf("mmc write failed\n\r"); + return err; + } + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + } + + return err; +} + +static ulong +mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void *src) +{ + int err; + int i; + struct mmc *mmc = find_mmc_device(dev_num); + uint b_max = mmc->b_max; + + if (!mmc) + return 0; + + for (i = blkcnt; i > 0; i -= b_max) { + uint blocks = (i > b_max) ? b_max : i; + + err = mmc_write_blocks(mmc, src, start, blocks); + if (err) + return blkcnt - i; + start += blocks; + src += (mmc->write_bl_len * blocks); + } + + return blkcnt; +} + +int mmc_read_blocks(struct mmc *mmc, void *dst, uint blocknum, uint blkcnt) +{ + int err; + struct mmc_cmd cmd; + struct mmc_data data; + + if (blkcnt > 1) + cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = blocknum; + else + cmd.cmdarg = blocknum * mmc->read_bl_len; + + cmd.resp_type = MMC_RSP_R1; + cmd.flags = 0; + + data.dest = dst; + data.blocks = blkcnt; + data.blocksize = mmc->read_bl_len; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + if (err) + return err; + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + } + + return err; +} + +static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +{ + int err; + int i; + struct mmc *mmc = find_mmc_device(dev_num); + uint b_max = mmc->b_max; + + if (!mmc) + return 0; + + /* We always do full block reads from the card */ + err = mmc_set_blocklen(mmc, mmc->read_bl_len); + if (err) + return 0; + + for (i = blkcnt; i > 0; i -= b_max) { + uint blocks = (i > b_max) ? b_max : i; + + err = mmc_read_blocks(mmc, dst, start, blocks); + if (err) { + printf("block read failed: %d\n", err); + return blkcnt - i; + } + start += blocks; + dst += (mmc->read_bl_len * blocks); + } + + return blkcnt; +} + +#endif + int mmc_go_idle(struct mmc* mmc) { struct mmc_cmd cmd; @@ -858,6 +1009,11 @@ int mmc_register(struct mmc *mmc) mmc->block_dev.block_read = mmc_bread; mmc->block_dev.block_write = mmc_bwrite;
+#ifdef CONFIG_MMC_MBLOCK + if (mmc->b_max == 0) + mmc->b_max = 1; +#endif + INIT_LIST_HEAD (&mmc->link);
list_add_tail (&mmc->link, &mmc_devices); diff --git a/include/mmc.h b/include/mmc.h index 8973bc7..04c7eaf 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -264,6 +264,9 @@ struct mmc { struct mmc_cmd *cmd, struct mmc_data *data); void (*set_ios)(struct mmc *mmc); int (*init)(struct mmc *mmc); +#ifdef CONFIG_MMC_MBLOCK + uint b_max; +#endif };
int mmc_register(struct mmc *mmc);