[U-Boot] [PATCH V2 1/5] mmc: sd: extracting erase timeout information from sd status

Add function to read SD_STATUS information. According to the information, get erase_timeout/erase_size/erase_offset. Add a structure sd_ssr to include the erase related information.
Signed-off-by: Peng Fan peng.fan@nxp.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Stefan Wahren stefan.wahren@i2se.com Cc: Clemens Gruber clemens.gruber@pqgruber.com Cc: Kever Yang kever.yang@rock-chips.com Cc: Eric Nelson eric@nelint.com Cc: Stephen Warren swarren@nvidia.com ---
V2: Address Jaehoon's comments for V1.
drivers/mmc/mmc.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 8 +++++++ 2 files changed, 76 insertions(+)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3daa748..a855acf 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -21,6 +21,14 @@ #include <div64.h> #include "mmc_private.h"
+static const unsigned int sd_au_size[] = { + 0, SZ_16K / 512, SZ_32K / 512, + SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, + SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, + SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, + SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, +}; + #ifndef CONFIG_DM_MMC_OPS __weak int board_mmc_getwp(struct mmc *mmc) { @@ -942,6 +950,62 @@ retry_scr: return 0; }
+static int sd_read_ssr(struct mmc *mmc) +{ + int err, i; + struct mmc_cmd cmd; + ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); + struct mmc_data data; + int timeout = 3; + unsigned int au, eo, et, es; + + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SD_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + +retry_ssr: + data.dest = (char *)ssr; + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + if (err) { + if (timeout--) + goto retry_ssr; + + return err; + } + + for (i = 0; i < 16; i++) + ssr[i] = be32_to_cpu(ssr[i]); + + au = (ssr[2] >> 12) & 0xF; + if ((au <= 9) || (mmc->version == SD_VERSION_3)) { + mmc->ssr.au = sd_au_size[au]; + es = (ssr[3] >> 24) & 0xFF; + es |= (ssr[2] & 0xFF) << 8; + et = (ssr[3] >> 18) & 0x3F; + if (es && et) { + eo = (ssr[3] >> 16) & 0x3; + mmc->ssr.erase_timeout = (et * 1000) / es; + mmc->ssr.erase_offset = eo * 1000; + } + } else { + debug("Invalid Allocation Unit Size.\n"); + } + + return 0; +} + /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ static const int fbase[] = { @@ -1347,6 +1411,10 @@ static int mmc_startup(struct mmc *mmc) mmc_set_bus_width(mmc, 4); }
+ err = sd_read_ssr(mmc); + if (err) + return err; + if (mmc->card_caps & MMC_MODE_HS) mmc->tran_speed = 50000000; else diff --git a/include/mmc.h b/include/mmc.h index aa6d5d1..af2595c 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -102,6 +102,7 @@ #define SD_CMD_SWITCH_UHS18V 11
#define SD_CMD_APP_SET_BUS_WIDTH 6 +#define SD_CMD_APP_SD_STATUS 13 #define SD_CMD_ERASE_WR_BLK_START 32 #define SD_CMD_ERASE_WR_BLK_END 33 #define SD_CMD_APP_SEND_OP_COND 41 @@ -392,6 +393,12 @@ struct mmc_config { unsigned char part_type; };
+struct sd_ssr { + unsigned int au; /* In sectors */ + unsigned int erase_timeout; /* In milliseconds */ + unsigned int erase_offset; /* In milliseconds */ +}; + /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device * with mmc_get_mmc_dev(). @@ -426,6 +433,7 @@ struct mmc { uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ uint hc_wp_grp_size; /* in 512-byte sectors */ + struct sd_ssr ssr; /* SD status register */ u64 capacity; u64 capacity_user; u64 capacity_boot;

Using {0} to initialize mmc_cmd, before filling the structure.
Signed-off-by: Peng Fan peng.fan@nxp.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Stefan Wahren stefan.wahren@i2se.com Cc: Clemens Gruber clemens.gruber@pqgruber.com Cc: Kever Yang kever.yang@rock-chips.com Cc: Eric Nelson eric@nelint.com Cc: Stephen Warren swarren@nvidia.com --- drivers/mmc/mmc.c | 28 ++++++++++++++-------------- drivers/mmc/mmc_write.c | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index a855acf..7a3a4f8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -140,7 +140,7 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
int mmc_send_status(struct mmc *mmc, int timeout) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int err, retries = 5;
cmd.cmdidx = MMC_CMD_SEND_STATUS; @@ -184,7 +184,7 @@ int mmc_send_status(struct mmc *mmc, int timeout)
int mmc_set_blocklen(struct mmc *mmc, int len) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0};
if (mmc->ddr_mode) return 0; @@ -199,7 +199,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len) static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; struct mmc_data data;
if (blkcnt > 1) @@ -292,7 +292,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
static int mmc_go_idle(struct mmc *mmc) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int err;
udelay(1000); @@ -315,7 +315,7 @@ static int sd_send_op_cond(struct mmc *mmc) { int timeout = 1000; int err; - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0};
while (1) { cmd.cmdidx = MMC_CMD_APP_CMD; @@ -381,7 +381,7 @@ static int sd_send_op_cond(struct mmc *mmc)
static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int err;
cmd.cmdidx = MMC_CMD_SEND_OP_COND; @@ -423,7 +423,7 @@ static int mmc_send_op_cond(struct mmc *mmc)
static int mmc_complete_op_cond(struct mmc *mmc) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int timeout = 1000; uint start; int err; @@ -467,7 +467,7 @@ static int mmc_complete_op_cond(struct mmc *mmc)
static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; struct mmc_data data; int err;
@@ -488,7 +488,7 @@ static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int timeout = 1000; int ret;
@@ -821,7 +821,7 @@ int mmc_getcd(struct mmc *mmc)
static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; struct mmc_data data;
/* Switch the frequency */ @@ -843,7 +843,7 @@ static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) static int sd_change_freq(struct mmc *mmc) { int err; - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); struct mmc_data data; @@ -953,7 +953,7 @@ retry_scr: static int sd_read_ssr(struct mmc *mmc) { int err, i; - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); struct mmc_data data; int timeout = 3; @@ -1070,7 +1070,7 @@ static int mmc_startup(struct mmc *mmc) int err, i; uint mult, freq; u64 cmult, csize, capacity; - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); int timeout = 1000; @@ -1553,7 +1553,7 @@ static int mmc_startup(struct mmc *mmc)
static int mmc_send_if_cond(struct mmc *mmc) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; int err;
cmd.cmdidx = SD_CMD_SEND_IF_COND; diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index 0f8b5c7..4149f4a 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -17,7 +17,7 @@
static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; ulong end; int err, start_cmd, end_cmd;
@@ -119,7 +119,7 @@ unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start, static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void *src) { - struct mmc_cmd cmd; + struct mmc_cmd cmd = {0}; struct mmc_data data; int timeout = 1000;

Add timeout in mmc_cmd, we can use this in driver code. Add mmc_sd_erase_timeout, this function is modified from linux kernel.
Signed-off-by: Peng Fan peng.fan@nxp.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Stefan Wahren stefan.wahren@i2se.com Cc: Clemens Gruber clemens.gruber@pqgruber.com Cc: Kever Yang kever.yang@rock-chips.com Cc: Eric Nelson eric@nelint.com Cc: Stephen Warren swarren@nvidia.com --- drivers/mmc/mmc_write.c | 29 +++++++++++++++++++++++++++++ include/mmc.h | 1 + 2 files changed, 30 insertions(+)
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index 4149f4a..3589f8e 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -15,6 +15,33 @@ #include <linux/math64.h> #include "mmc_private.h"
+/* + * Modified from from Linux kernel mmc_sd_erase_timeout. + */ +static unsigned int mmc_sd_erase_timeout(struct mmc *mmc, + unsigned int nr) +{ + unsigned int erase_timeout; + + if (mmc->ssr.erase_timeout) { + /* Erase timeout specified in SD Status Register (SSR) */ + erase_timeout = mmc->ssr.erase_timeout * nr + + mmc->ssr.erase_offset; + } else { + /* + * Erase timeout not specified in SD Status Register (SSR) so + * use 250ms per write block. + */ + erase_timeout = 250 * nr; + } + + /* Must not be less than 1 second */ + if (erase_timeout < 1000) + erase_timeout = 1000; + + return erase_timeout; +} + static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) { struct mmc_cmd cmd = {0}; @@ -54,6 +81,8 @@ static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) cmd.cmdidx = MMC_CMD_ERASE; cmd.cmdarg = MMC_ERASE_ARG; cmd.resp_type = MMC_RSP_R1b; + if (IS_SD(mmc)) + cmd.timeout = mmc_sd_erase_timeout(mmc, blkcnt);
err = mmc_send_cmd(mmc, &cmd, NULL); if (err) diff --git a/include/mmc.h b/include/mmc.h index af2595c..432e6ee 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -303,6 +303,7 @@ struct mmc_cmd { uint resp_type; uint cmdarg; uint response[4]; + int timeout; };
struct mmc_data {

Change timeout according to the timeout value in mmc_cmd->timeout.
Signed-off-by: Peng Fan peng.fan@nxp.com Cc: Jaehoon Chung jh80.chung@samsung.com --- drivers/mmc/fsl_esdhc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 9796d39..eca2f31 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -350,6 +350,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) int err = 0; uint xfertyp; uint irqstat; + int timeout = cmd->timeout; struct fsl_esdhc_priv *priv = mmc->priv; struct fsl_esdhc *regs = priv->esdhc_regs;
@@ -431,7 +432,8 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
/* Workaround for ESDHC errata ENGcm03648 */ if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { - int timeout = 6000; + if (timeout < 6000) + timeout = 6000;
/* Poll on DATA0 line for cmd with busy signal for 600 ms */ while (timeout > 0 && !(esdhc_read32(®s->prsstat) &

To SD, there is no erase group, then the value erase_grp_size will be default 1. When erasing SD blocks, the blocks will be erased one by one, which is time consuming.
We use AU_SIZE as a group to speed up the erasing.
Erasing 4MB with a SD2.0 Card with AU_SIZE 4MB. `time mmc erase 0x100000 0x2000` time: 44.856 seconds (before optimization) time: 0.335 seconds (after optimization)
Signed-off-by: Peng Fan peng.fan@nxp.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Stefan Wahren stefan.wahren@i2se.com Cc: Clemens Gruber clemens.gruber@pqgruber.com Cc: Kever Yang kever.yang@rock-chips.com Cc: Eric Nelson eric@nelint.com Cc: Stephen Warren swarren@nvidia.com ---
V2: Add more commit log
drivers/mmc/mmc_write.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index 3589f8e..6221b4a 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -129,8 +129,13 @@ unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start, & ~(mmc->erase_grp_size - 1)) - 1);
while (blk < blkcnt) { - blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? - mmc->erase_grp_size : (blkcnt - blk); + if (IS_SD(mmc) && mmc->ssr.au) { + blk_r = ((blkcnt - blk) > mmc->ssr.au) ? + mmc->ssr.au : (blkcnt - blk); + } else { + 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;
participants (1)
-
Peng Fan