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 /drivers/mmc | |
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>
Diffstat (limited to 'drivers/mmc')
-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 | } |