Commit 8dc8d294b7f6ff273fffbe42adbf41affe2385bc
Committed by
Lokesh Vutla
1 parent
7a0a68ac5b
Exists in
v2017.01-smarct4x
and in
2 other branches
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
drivers/mmc/mmc.c
... | ... | @@ -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; |