Commit 8dc8d294b7f6ff273fffbe42adbf41affe2385bc

Authored by Jean-Jacques Hiblot
Committed by Lokesh Vutla
1 parent 7a0a68ac5b

drivers: mmc: downgrade the current mode if error rate becomes too high

Use a basic heuristic based on the transfers statistics to downgrade the
current mode (HS200->DDR52->HS) when errors occur.
Current heuristic is: if error rate is more than 20% and the number of
transfers is large enough to be significant, then downgrade.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>

Showing 3 changed files with 72 additions and 4 deletions Side-by-side Diff

... ... @@ -22,6 +22,8 @@
22 22 #include <div64.h>
23 23 #include "mmc_private.h"
24 24  
  25 +#define MAX_ERROR_RATE 20
  26 +
25 27 static const unsigned int sd_au_size[] = {
26 28 0, SZ_16K / 512, SZ_32K / 512,
27 29 SZ_64K / 512, SZ_128K / 512, SZ_256K / 512,
... ... @@ -29,6 +31,7 @@
29 31 SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512,
30 32 SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512,
31 33 };
  34 +static int mmc_reinit(struct mmc *mmc);
32 35 static int mmc_set_timing(struct mmc *mmc, uint timing);
33 36 static int mmc_set_bus_width(struct mmc *mmc, uint width);
34 37 static int mmc_select_bus_width(struct mmc *mmc);
... ... @@ -349,6 +352,43 @@
349 352 return blkcnt;
350 353 }
351 354  
  355 +bool mmc_check_error_rate(struct mmc *mmc, struct mmc_statistics *s)
  356 +{
  357 + int percent = s->transfers ? ((s->errors * 100) / s->transfers) : 0;
  358 +
  359 + if ((percent > MAX_ERROR_RATE) && (s->transfers > 10)) {
  360 + debug("error rate too high: %d%% (%d/%d)\n", percent,
  361 + s->errors, s->transfers);
  362 + return true;
  363 + }
  364 + return false;
  365 +}
  366 +
  367 +bool mmc_disable_current_mode(struct mmc *mmc)
  368 +{
  369 + __maybe_unused const char *mode;
  370 + bool disabled = true;
  371 +
  372 + switch (mmc->timing) {
  373 + case MMC_TIMING_MMC_HS200:
  374 + mode = "HS200";
  375 + mmc->host_ok_caps &= ~MMC_MODE_HS200;
  376 + break;
  377 + case MMC_TIMING_MMC_DDR52:
  378 + mode = "DDR52";
  379 + mmc->host_ok_caps &= ~MMC_MODE_DDR_52MHz;
  380 + break;
  381 + default:
  382 + disabled = false;
  383 + }
  384 + if (disabled) {
  385 + debug("%s mode is disabled. Reinitializing the MMC...\n", mode);
  386 + mmc->has_init = 0;
  387 + mmc_reinit(mmc);
  388 + }
  389 + return disabled;
  390 +}
  391 +
352 392 #ifdef CONFIG_BLK
353 393 ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
354 394 #else
... ... @@ -399,6 +439,15 @@
399 439 mmc->cfg->b_max : blocks_todo;
400 440 if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
401 441 mmc->rd_stats.errors++;
  442 + /*
  443 + * An error occured. Maybe we should try a slower but
  444 + * safer mode.
  445 + */
  446 + if (mmc_check_error_rate(mmc, &mmc->rd_stats))
  447 + if (mmc_disable_current_mode(mmc))
  448 + return (blkcnt - blocks_todo) +
  449 + mmc_bread(block_dev, start,
  450 + blocks_todo, dst);
402 451 debug("%s: Failed to read blocks\n", __func__);
403 452 return 0;
404 453 }
... ... @@ -2235,7 +2284,7 @@
2235 2284 return err;
2236 2285 }
2237 2286  
2238   -int mmc_init(struct mmc *mmc)
  2287 +static int mmc_reinit(struct mmc *mmc)
2239 2288 {
2240 2289 int err = 0;
2241 2290 int retries = 0;
2242 2291  
... ... @@ -2245,10 +2294,9 @@
2245 2294  
2246 2295 upriv->mmc = mmc;
2247 2296 #endif
2248   - if (mmc->has_init)
2249   - return 0;
2250 2297  
2251   - mmc->host_ok_caps = mmc->cfg->host_caps;
  2298 + memset(&mmc->rd_stats, 0, sizeof(struct mmc_statistics));
  2299 + memset(&mmc->wr_stats, 0, sizeof(struct mmc_statistics));
2252 2300 start = get_timer(0);
2253 2301  
2254 2302 do {
... ... @@ -2263,6 +2311,14 @@
2263 2311 debug("%s: %d, time %lu (retries %d)\n", __func__, err,
2264 2312 get_timer(start), retries - 1);
2265 2313 return err;
  2314 +}
  2315 +
  2316 +int mmc_init(struct mmc *mmc)
  2317 +{
  2318 + if (mmc->has_init)
  2319 + return 0;
  2320 + mmc->host_ok_caps = mmc->cfg->host_caps;
  2321 + return mmc_reinit(mmc);
2266 2322 }
2267 2323  
2268 2324 int mmc_set_dsr(struct mmc *mmc, u16 val)
drivers/mmc/mmc_private.h
... ... @@ -12,6 +12,8 @@
12 12  
13 13 #include <mmc.h>
14 14  
  15 +bool mmc_check_error_rate(struct mmc *mmc, struct mmc_statistics *stats);
  16 +bool mmc_disable_current_mode(struct mmc *mmc);
15 17 extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
16 18 struct mmc_data *data);
17 19 extern int mmc_send_status(struct mmc *mmc, int timeout);
drivers/mmc/mmc_write.c
... ... @@ -218,6 +218,16 @@
218 218 mmc->cfg->b_max : blocks_todo;
219 219 if (mmc_write_blocks(mmc, start, cur, src) != cur) {
220 220 mmc->wr_stats.errors++;
  221 + /*
  222 + * An error occured. Maybe we should try a slower but
  223 + * safer mode.
  224 + */
  225 + if (mmc_check_error_rate(mmc, &mmc->wr_stats))
  226 + if (mmc_disable_current_mode(mmc))
  227 + return (blkcnt - blocks_todo) +
  228 + mmc_bwrite(block_dev, start,
  229 + blocks_todo, src);
  230 + debug("%s: Failed to write blocks\n", __func__);
221 231 return 0;
222 232 }
223 233 blocks_todo -= cur;