diff options
| author | Philip Rakity <prakity@marvell.com> | 2011-05-24 21:14:58 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2011-05-25 16:52:32 -0400 |
| commit | 08ee80cc397ac1a306ca689a22ede954d92d0db1 (patch) | |
| tree | b36819e6b52c9fd939dd6c886af734cb6ccbc45f | |
| parent | 4f3d3e9b50931a3ec70f324d53fb7437e2a39388 (diff) | |
mmc: core: eMMC bus width may not work on all platforms
CMD19 -- The offical way to validate bus widths from the JEDEC spec
does not work on all platforms. Some platforms that use PCI/PCIe
to connect their SD controllers are known to fail.
If the quirk MMC_BUS_WIDTH_TEST is not defined we try to figure out
the bus width by reading the ext_csd at different bus widths and
compare this against the ext_csd read in 1 bit mode. If no ext_csd
is available we default to 1 bit operations.
Code has been tested on mmp2 against 8 bit eMMC and Transcend 2GB
card that is known to not work in 4 bit mode. The physical pins
on the card are not present to support 4 bit operation.
Signed-off-by: Philip Rakity <prakity@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
| -rw-r--r-- | drivers/mmc/core/mmc.c | 112 |
1 files changed, 102 insertions, 10 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0433fe66cbad..2a7e43bc796d 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
| @@ -174,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card) | |||
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | /* | 176 | /* |
| 177 | * Read and decode extended CSD. | 177 | * Read extended CSD. |
| 178 | */ | 178 | */ |
| 179 | static int mmc_read_ext_csd(struct mmc_card *card) | 179 | static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) |
| 180 | { | 180 | { |
| 181 | int err; | 181 | int err; |
| 182 | u8 *ext_csd; | 182 | u8 *ext_csd; |
| 183 | 183 | ||
| 184 | BUG_ON(!card); | 184 | BUG_ON(!card); |
| 185 | BUG_ON(!new_ext_csd); | ||
| 186 | |||
| 187 | *new_ext_csd = NULL; | ||
| 185 | 188 | ||
| 186 | if (card->csd.mmca_vsn < CSD_SPEC_VER_4) | 189 | if (card->csd.mmca_vsn < CSD_SPEC_VER_4) |
| 187 | return 0; | 190 | return 0; |
| @@ -199,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
| 199 | 202 | ||
| 200 | err = mmc_send_ext_csd(card, ext_csd); | 203 | err = mmc_send_ext_csd(card, ext_csd); |
| 201 | if (err) { | 204 | if (err) { |
| 205 | kfree(ext_csd); | ||
| 206 | *new_ext_csd = NULL; | ||
| 207 | |||
| 202 | /* If the host or the card can't do the switch, | 208 | /* If the host or the card can't do the switch, |
| 203 | * fail more gracefully. */ | 209 | * fail more gracefully. */ |
| 204 | if ((err != -EINVAL) | 210 | if ((err != -EINVAL) |
| 205 | && (err != -ENOSYS) | 211 | && (err != -ENOSYS) |
| 206 | && (err != -EFAULT)) | 212 | && (err != -EFAULT)) |
| 207 | goto out; | 213 | return err; |
| 208 | 214 | ||
| 209 | /* | 215 | /* |
| 210 | * High capacity cards should have this "magic" size | 216 | * High capacity cards should have this "magic" size |
| @@ -222,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
| 222 | mmc_hostname(card->host)); | 228 | mmc_hostname(card->host)); |
| 223 | err = 0; | 229 | err = 0; |
| 224 | } | 230 | } |
| 231 | } else | ||
| 232 | *new_ext_csd = ext_csd; | ||
| 225 | 233 | ||
| 226 | goto out; | 234 | return err; |
| 227 | } | 235 | } |
| 236 | |||
| 237 | /* | ||
| 238 | * Decode extended CSD. | ||
| 239 | */ | ||
| 240 | static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | ||
| 241 | { | ||
| 242 | int err = 0; | ||
| 243 | |||
| 244 | BUG_ON(!card); | ||
| 245 | |||
| 246 | if (!ext_csd) | ||
| 247 | return 0; | ||
| 228 | 248 | ||
| 229 | /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ | 249 | /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ |
| 230 | if (card->csd.structure == 3) { | 250 | if (card->csd.structure == 3) { |
| @@ -372,8 +392,69 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
| 372 | card->erased_byte = 0x0; | 392 | card->erased_byte = 0x0; |
| 373 | 393 | ||
| 374 | out: | 394 | out: |
| 395 | return err; | ||
| 396 | } | ||
| 397 | |||
| 398 | static inline void mmc_free_ext_csd(u8 *ext_csd) | ||
| 399 | { | ||
| 375 | kfree(ext_csd); | 400 | kfree(ext_csd); |
| 401 | } | ||
| 402 | |||
| 376 | 403 | ||
| 404 | static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd, | ||
| 405 | unsigned bus_width) | ||
| 406 | { | ||
| 407 | u8 *bw_ext_csd; | ||
| 408 | int err; | ||
| 409 | |||
| 410 | err = mmc_get_ext_csd(card, &bw_ext_csd); | ||
| 411 | if (err) | ||
| 412 | return err; | ||
| 413 | |||
| 414 | if ((ext_csd == NULL || bw_ext_csd == NULL)) { | ||
| 415 | if (bus_width != MMC_BUS_WIDTH_1) | ||
| 416 | err = -EINVAL; | ||
| 417 | goto out; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (bus_width == MMC_BUS_WIDTH_1) | ||
| 421 | goto out; | ||
| 422 | |||
| 423 | /* only compare read only fields */ | ||
| 424 | err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] == | ||
| 425 | bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && | ||
| 426 | (ext_csd[EXT_CSD_ERASED_MEM_CONT] == | ||
| 427 | bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && | ||
| 428 | (ext_csd[EXT_CSD_REV] == | ||
| 429 | bw_ext_csd[EXT_CSD_REV]) && | ||
| 430 | (ext_csd[EXT_CSD_STRUCTURE] == | ||
| 431 | bw_ext_csd[EXT_CSD_STRUCTURE]) && | ||
| 432 | (ext_csd[EXT_CSD_CARD_TYPE] == | ||
| 433 | bw_ext_csd[EXT_CSD_CARD_TYPE]) && | ||
| 434 | (ext_csd[EXT_CSD_S_A_TIMEOUT] == | ||
| 435 | bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && | ||
| 436 | (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == | ||
| 437 | bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && | ||
| 438 | (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == | ||
| 439 | bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && | ||
| 440 | (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == | ||
| 441 | bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && | ||
| 442 | (ext_csd[EXT_CSD_SEC_TRIM_MULT] == | ||
| 443 | bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && | ||
| 444 | (ext_csd[EXT_CSD_SEC_ERASE_MULT] == | ||
| 445 | bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && | ||
| 446 | (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == | ||
| 447 | bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && | ||
| 448 | (ext_csd[EXT_CSD_TRIM_MULT] == | ||
| 449 | bw_ext_csd[EXT_CSD_TRIM_MULT]) && | ||
| 450 | memcmp(&ext_csd[EXT_CSD_SEC_CNT], | ||
| 451 | &bw_ext_csd[EXT_CSD_SEC_CNT], | ||
| 452 | 4) != 0); | ||
| 453 | if (err) | ||
| 454 | err = -EINVAL; | ||
| 455 | |||
| 456 | out: | ||
| 457 | mmc_free_ext_csd(bw_ext_csd); | ||
| 377 | return err; | 458 | return err; |
| 378 | } | 459 | } |
| 379 | 460 | ||
| @@ -438,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 438 | u32 cid[4]; | 519 | u32 cid[4]; |
| 439 | unsigned int max_dtr; | 520 | unsigned int max_dtr; |
| 440 | u32 rocr; | 521 | u32 rocr; |
| 522 | u8 *ext_csd = NULL; | ||
| 441 | 523 | ||
| 442 | BUG_ON(!host); | 524 | BUG_ON(!host); |
| 443 | WARN_ON(!host->claimed); | 525 | WARN_ON(!host->claimed); |
| @@ -536,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 536 | /* | 618 | /* |
| 537 | * Fetch and process extended CSD. | 619 | * Fetch and process extended CSD. |
| 538 | */ | 620 | */ |
| 539 | err = mmc_read_ext_csd(card); | 621 | |
| 622 | err = mmc_get_ext_csd(card, &ext_csd); | ||
| 623 | if (err) | ||
| 624 | goto free_card; | ||
| 625 | err = mmc_read_ext_csd(card, ext_csd); | ||
| 540 | if (err) | 626 | if (err) |
| 541 | goto free_card; | 627 | goto free_card; |
| 542 | 628 | ||
| @@ -676,14 +762,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 676 | 0); | 762 | 0); |
| 677 | if (!err) { | 763 | if (!err) { |
| 678 | mmc_set_bus_width(card->host, bus_width); | 764 | mmc_set_bus_width(card->host, bus_width); |
| 765 | |||
| 679 | /* | 766 | /* |
| 680 | * If controller can't handle bus width test, | 767 | * If controller can't handle bus width test, |
| 681 | * use the highest bus width to maintain | 768 | * compare ext_csd previously read in 1 bit mode |
| 682 | * compatibility with previous MMC behavior. | 769 | * against ext_csd at new bus width |
| 683 | */ | 770 | */ |
| 684 | if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) | 771 | if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) |
| 685 | break; | 772 | err = mmc_compare_ext_csds(card, |
| 686 | err = mmc_bus_test(card, bus_width); | 773 | ext_csd, |
| 774 | bus_width); | ||
| 775 | else | ||
| 776 | err = mmc_bus_test(card, bus_width); | ||
| 687 | if (!err) | 777 | if (!err) |
| 688 | break; | 778 | break; |
| 689 | } | 779 | } |
| @@ -730,12 +820,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 730 | if (!oldcard) | 820 | if (!oldcard) |
| 731 | host->card = card; | 821 | host->card = card; |
| 732 | 822 | ||
| 823 | mmc_free_ext_csd(ext_csd); | ||
| 733 | return 0; | 824 | return 0; |
| 734 | 825 | ||
| 735 | free_card: | 826 | free_card: |
| 736 | if (!oldcard) | 827 | if (!oldcard) |
| 737 | mmc_remove_card(card); | 828 | mmc_remove_card(card); |
| 738 | err: | 829 | err: |
| 830 | mmc_free_ext_csd(ext_csd); | ||
| 739 | 831 | ||
| 740 | return err; | 832 | return err; |
| 741 | } | 833 | } |
