diff options
| author | Philip Rakity <prakity@marvell.com> | 2011-07-07 12:04:55 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2011-07-13 14:54:37 -0400 |
| commit | f39b2dd9d065151a04f5996656d1f27a7eb32d45 (patch) | |
| tree | 3801f7d6793529d737fcbfd6af05b6900ef2cd06 | |
| parent | c31b55cd4eaf050bb5a15bd8251da1b3c7edeb1c (diff) | |
mmc: core: Bus width testing needs to handle suspend/resume
On reading the ext_csd for the first time (in 1 bit mode), save the
ext_csd information needed for bus width compare.
On every pass we make re-reading the ext_csd, compare the data
against the saved ext_csd data.
This fixes a regression introduced in 3.0-rc1 by 08ee80cc397ac1a3
("mmc: core: eMMC bus width may not work on all platforms"), which
incorrectly assumed we would be re-reading the ext_csd at resume-
time.
Signed-off-by: Philip Rakity <prakity@marvell.com>
Tested-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
| -rw-r--r-- | drivers/mmc/core/mmc.c | 77 | ||||
| -rw-r--r-- | include/linux/mmc/card.h | 13 |
2 files changed, 63 insertions, 27 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 2a7e43bc796d..aa7d1d79b8c5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
| @@ -247,12 +247,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 247 | return 0; | 247 | return 0; |
| 248 | 248 | ||
| 249 | /* 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 */ |
| 250 | card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; | ||
| 250 | if (card->csd.structure == 3) { | 251 | if (card->csd.structure == 3) { |
| 251 | int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE]; | 252 | if (card->ext_csd.raw_ext_csd_structure > 2) { |
| 252 | if (ext_csd_struct > 2) { | ||
| 253 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " | 253 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " |
| 254 | "version %d\n", mmc_hostname(card->host), | 254 | "version %d\n", mmc_hostname(card->host), |
| 255 | ext_csd_struct); | 255 | card->ext_csd.raw_ext_csd_structure); |
| 256 | err = -EINVAL; | 256 | err = -EINVAL; |
| 257 | goto out; | 257 | goto out; |
| 258 | } | 258 | } |
| @@ -266,6 +266,10 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 266 | goto out; | 266 | goto out; |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; | ||
| 270 | card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; | ||
| 271 | card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2]; | ||
| 272 | card->ext_csd.raw_sectors[3] = ext_csd[EXT_CSD_SEC_CNT + 3]; | ||
| 269 | if (card->ext_csd.rev >= 2) { | 273 | if (card->ext_csd.rev >= 2) { |
| 270 | card->ext_csd.sectors = | 274 | card->ext_csd.sectors = |
| 271 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | | 275 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | |
| @@ -277,7 +281,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 277 | if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) | 281 | if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) |
| 278 | mmc_card_set_blockaddr(card); | 282 | mmc_card_set_blockaddr(card); |
| 279 | } | 283 | } |
| 280 | 284 | card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; | |
| 281 | switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { | 285 | switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { |
| 282 | case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 | | 286 | case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 | |
| 283 | EXT_CSD_CARD_TYPE_26: | 287 | EXT_CSD_CARD_TYPE_26: |
| @@ -307,6 +311,11 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 307 | mmc_hostname(card->host)); | 311 | mmc_hostname(card->host)); |
| 308 | } | 312 | } |
| 309 | 313 | ||
| 314 | card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT]; | ||
| 315 | card->ext_csd.raw_erase_timeout_mult = | ||
| 316 | ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; | ||
| 317 | card->ext_csd.raw_hc_erase_grp_size = | ||
| 318 | ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; | ||
| 310 | if (card->ext_csd.rev >= 3) { | 319 | if (card->ext_csd.rev >= 3) { |
| 311 | u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; | 320 | u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; |
| 312 | card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG]; | 321 | card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG]; |
| @@ -334,6 +343,16 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 334 | card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; | 343 | card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; |
| 335 | } | 344 | } |
| 336 | 345 | ||
| 346 | card->ext_csd.raw_hc_erase_gap_size = | ||
| 347 | ext_csd[EXT_CSD_PARTITION_ATTRIBUTE]; | ||
| 348 | card->ext_csd.raw_sec_trim_mult = | ||
| 349 | ext_csd[EXT_CSD_SEC_TRIM_MULT]; | ||
| 350 | card->ext_csd.raw_sec_erase_mult = | ||
| 351 | ext_csd[EXT_CSD_SEC_ERASE_MULT]; | ||
| 352 | card->ext_csd.raw_sec_feature_support = | ||
| 353 | ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; | ||
| 354 | card->ext_csd.raw_trim_mult = | ||
| 355 | ext_csd[EXT_CSD_TRIM_MULT]; | ||
| 337 | if (card->ext_csd.rev >= 4) { | 356 | if (card->ext_csd.rev >= 4) { |
| 338 | /* | 357 | /* |
| 339 | * Enhanced area feature support -- check whether the eMMC | 358 | * Enhanced area feature support -- check whether the eMMC |
| @@ -341,7 +360,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 341 | * area offset and size to user by adding sysfs interface. | 360 | * area offset and size to user by adding sysfs interface. |
| 342 | */ | 361 | */ |
| 343 | if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && | 362 | if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && |
| 344 | (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { | 363 | (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { |
| 345 | u8 hc_erase_grp_sz = | 364 | u8 hc_erase_grp_sz = |
| 346 | ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; | 365 | ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; |
| 347 | u8 hc_wp_grp_sz = | 366 | u8 hc_wp_grp_sz = |
| @@ -401,17 +420,17 @@ static inline void mmc_free_ext_csd(u8 *ext_csd) | |||
| 401 | } | 420 | } |
| 402 | 421 | ||
| 403 | 422 | ||
| 404 | static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd, | 423 | static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) |
| 405 | unsigned bus_width) | ||
| 406 | { | 424 | { |
| 407 | u8 *bw_ext_csd; | 425 | u8 *bw_ext_csd; |
| 408 | int err; | 426 | int err; |
| 409 | 427 | ||
| 428 | if (bus_width == MMC_BUS_WIDTH_1) | ||
| 429 | return 0; | ||
| 430 | |||
| 410 | err = mmc_get_ext_csd(card, &bw_ext_csd); | 431 | err = mmc_get_ext_csd(card, &bw_ext_csd); |
| 411 | if (err) | ||
| 412 | return err; | ||
| 413 | 432 | ||
| 414 | if ((ext_csd == NULL || bw_ext_csd == NULL)) { | 433 | if (err || bw_ext_csd == NULL) { |
| 415 | if (bus_width != MMC_BUS_WIDTH_1) | 434 | if (bus_width != MMC_BUS_WIDTH_1) |
| 416 | err = -EINVAL; | 435 | err = -EINVAL; |
| 417 | goto out; | 436 | goto out; |
| @@ -421,35 +440,40 @@ static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd, | |||
| 421 | goto out; | 440 | goto out; |
| 422 | 441 | ||
| 423 | /* only compare read only fields */ | 442 | /* only compare read only fields */ |
| 424 | err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] == | 443 | err = (!(card->ext_csd.raw_partition_support == |
| 425 | bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && | 444 | bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && |
| 426 | (ext_csd[EXT_CSD_ERASED_MEM_CONT] == | 445 | (card->ext_csd.raw_erased_mem_count == |
| 427 | bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && | 446 | bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && |
| 428 | (ext_csd[EXT_CSD_REV] == | 447 | (card->ext_csd.rev == |
| 429 | bw_ext_csd[EXT_CSD_REV]) && | 448 | bw_ext_csd[EXT_CSD_REV]) && |
| 430 | (ext_csd[EXT_CSD_STRUCTURE] == | 449 | (card->ext_csd.raw_ext_csd_structure == |
| 431 | bw_ext_csd[EXT_CSD_STRUCTURE]) && | 450 | bw_ext_csd[EXT_CSD_STRUCTURE]) && |
| 432 | (ext_csd[EXT_CSD_CARD_TYPE] == | 451 | (card->ext_csd.raw_card_type == |
| 433 | bw_ext_csd[EXT_CSD_CARD_TYPE]) && | 452 | bw_ext_csd[EXT_CSD_CARD_TYPE]) && |
| 434 | (ext_csd[EXT_CSD_S_A_TIMEOUT] == | 453 | (card->ext_csd.raw_s_a_timeout == |
| 435 | bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && | 454 | bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && |
| 436 | (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == | 455 | (card->ext_csd.raw_hc_erase_gap_size == |
| 437 | bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && | 456 | bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && |
| 438 | (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == | 457 | (card->ext_csd.raw_erase_timeout_mult == |
| 439 | bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && | 458 | bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && |
| 440 | (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == | 459 | (card->ext_csd.raw_hc_erase_grp_size == |
| 441 | bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && | 460 | bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && |
| 442 | (ext_csd[EXT_CSD_SEC_TRIM_MULT] == | 461 | (card->ext_csd.raw_sec_trim_mult == |
| 443 | bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && | 462 | bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && |
| 444 | (ext_csd[EXT_CSD_SEC_ERASE_MULT] == | 463 | (card->ext_csd.raw_sec_erase_mult == |
| 445 | bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && | 464 | bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && |
| 446 | (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == | 465 | (card->ext_csd.raw_sec_feature_support == |
| 447 | bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && | 466 | bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && |
| 448 | (ext_csd[EXT_CSD_TRIM_MULT] == | 467 | (card->ext_csd.raw_trim_mult == |
| 449 | bw_ext_csd[EXT_CSD_TRIM_MULT]) && | 468 | bw_ext_csd[EXT_CSD_TRIM_MULT]) && |
| 450 | memcmp(&ext_csd[EXT_CSD_SEC_CNT], | 469 | (card->ext_csd.raw_sectors[0] == |
| 451 | &bw_ext_csd[EXT_CSD_SEC_CNT], | 470 | bw_ext_csd[EXT_CSD_SEC_CNT + 0]) && |
| 452 | 4) != 0); | 471 | (card->ext_csd.raw_sectors[1] == |
| 472 | bw_ext_csd[EXT_CSD_SEC_CNT + 1]) && | ||
| 473 | (card->ext_csd.raw_sectors[2] == | ||
| 474 | bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && | ||
| 475 | (card->ext_csd.raw_sectors[3] == | ||
| 476 | bw_ext_csd[EXT_CSD_SEC_CNT + 3])); | ||
| 453 | if (err) | 477 | if (err) |
| 454 | err = -EINVAL; | 478 | err = -EINVAL; |
| 455 | 479 | ||
| @@ -770,7 +794,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 770 | */ | 794 | */ |
| 771 | if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) | 795 | if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) |
| 772 | err = mmc_compare_ext_csds(card, | 796 | err = mmc_compare_ext_csds(card, |
| 773 | ext_csd, | ||
| 774 | bus_width); | 797 | bus_width); |
| 775 | else | 798 | else |
| 776 | err = mmc_bus_test(card, bus_width); | 799 | err = mmc_bus_test(card, bus_width); |
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c6927a4d157f..6ad43554ac05 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h | |||
| @@ -64,6 +64,19 @@ struct mmc_ext_csd { | |||
| 64 | unsigned long long enhanced_area_offset; /* Units: Byte */ | 64 | unsigned long long enhanced_area_offset; /* Units: Byte */ |
| 65 | unsigned int enhanced_area_size; /* Units: KB */ | 65 | unsigned int enhanced_area_size; /* Units: KB */ |
| 66 | unsigned int boot_size; /* in bytes */ | 66 | unsigned int boot_size; /* in bytes */ |
| 67 | u8 raw_partition_support; /* 160 */ | ||
| 68 | u8 raw_erased_mem_count; /* 181 */ | ||
| 69 | u8 raw_ext_csd_structure; /* 194 */ | ||
| 70 | u8 raw_card_type; /* 196 */ | ||
| 71 | u8 raw_s_a_timeout; /* 217 */ | ||
| 72 | u8 raw_hc_erase_gap_size; /* 221 */ | ||
| 73 | u8 raw_erase_timeout_mult; /* 223 */ | ||
| 74 | u8 raw_hc_erase_grp_size; /* 224 */ | ||
| 75 | u8 raw_sec_trim_mult; /* 229 */ | ||
| 76 | u8 raw_sec_erase_mult; /* 230 */ | ||
| 77 | u8 raw_sec_feature_support;/* 231 */ | ||
| 78 | u8 raw_trim_mult; /* 232 */ | ||
| 79 | u8 raw_sectors[4]; /* 212 - 4 bytes */ | ||
| 67 | }; | 80 | }; |
| 68 | 81 | ||
| 69 | struct sd_scr { | 82 | struct sd_scr { |
