diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-02-22 14:05:19 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-02-22 14:05:19 -0500 |
| commit | fc6fc7f1b1095b92d4834e69b385b91e412a7ce5 (patch) | |
| tree | 2ad451d5dac4d460830536944cef1de93be36b2a /drivers/mmc/host/omap_hsmmc.c | |
| parent | ef1f87aa7ba6224bef1b750b3272ba281d8f43ed (diff) | |
| parent | 770824bdc421ff58a64db608294323571c949f4c (diff) | |
Merge branch 'linus' into x86/apic
Conflicts:
arch/x86/mach-default/setup.c
Semantic conflict resolution:
arch/x86/kernel/setup.c
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
| -rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 98 |
1 files changed, 68 insertions, 30 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index db37490f67ec..a631c81dce12 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c | |||
| @@ -55,6 +55,7 @@ | |||
| 55 | #define VS30 (1 << 25) | 55 | #define VS30 (1 << 25) |
| 56 | #define SDVS18 (0x5 << 9) | 56 | #define SDVS18 (0x5 << 9) |
| 57 | #define SDVS30 (0x6 << 9) | 57 | #define SDVS30 (0x6 << 9) |
| 58 | #define SDVS33 (0x7 << 9) | ||
| 58 | #define SDVSCLR 0xFFFFF1FF | 59 | #define SDVSCLR 0xFFFFF1FF |
| 59 | #define SDVSDET 0x00000400 | 60 | #define SDVSDET 0x00000400 |
| 60 | #define AUTOIDLE 0x1 | 61 | #define AUTOIDLE 0x1 |
| @@ -375,6 +376,32 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) | |||
| 375 | } | 376 | } |
| 376 | #endif /* CONFIG_MMC_DEBUG */ | 377 | #endif /* CONFIG_MMC_DEBUG */ |
| 377 | 378 | ||
| 379 | /* | ||
| 380 | * MMC controller internal state machines reset | ||
| 381 | * | ||
| 382 | * Used to reset command or data internal state machines, using respectively | ||
| 383 | * SRC or SRD bit of SYSCTL register | ||
| 384 | * Can be called from interrupt context | ||
| 385 | */ | ||
| 386 | static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host, | ||
| 387 | unsigned long bit) | ||
| 388 | { | ||
| 389 | unsigned long i = 0; | ||
| 390 | unsigned long limit = (loops_per_jiffy * | ||
| 391 | msecs_to_jiffies(MMC_TIMEOUT_MS)); | ||
| 392 | |||
| 393 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
| 394 | OMAP_HSMMC_READ(host->base, SYSCTL) | bit); | ||
| 395 | |||
| 396 | while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && | ||
| 397 | (i++ < limit)) | ||
| 398 | cpu_relax(); | ||
| 399 | |||
| 400 | if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) | ||
| 401 | dev_err(mmc_dev(host->mmc), | ||
| 402 | "Timeout waiting on controller reset in %s\n", | ||
| 403 | __func__); | ||
| 404 | } | ||
| 378 | 405 | ||
| 379 | /* | 406 | /* |
| 380 | * MMC controller IRQ handler | 407 | * MMC controller IRQ handler |
| @@ -403,21 +430,17 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 403 | (status & CMD_CRC)) { | 430 | (status & CMD_CRC)) { |
| 404 | if (host->cmd) { | 431 | if (host->cmd) { |
| 405 | if (status & CMD_TIMEOUT) { | 432 | if (status & CMD_TIMEOUT) { |
| 406 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | 433 | mmc_omap_reset_controller_fsm(host, SRC); |
| 407 | OMAP_HSMMC_READ(host->base, | ||
| 408 | SYSCTL) | SRC); | ||
| 409 | while (OMAP_HSMMC_READ(host->base, | ||
| 410 | SYSCTL) & SRC) | ||
| 411 | ; | ||
| 412 | |||
| 413 | host->cmd->error = -ETIMEDOUT; | 434 | host->cmd->error = -ETIMEDOUT; |
| 414 | } else { | 435 | } else { |
| 415 | host->cmd->error = -EILSEQ; | 436 | host->cmd->error = -EILSEQ; |
| 416 | } | 437 | } |
| 417 | end_cmd = 1; | 438 | end_cmd = 1; |
| 418 | } | 439 | } |
| 419 | if (host->data) | 440 | if (host->data) { |
| 420 | mmc_dma_cleanup(host); | 441 | mmc_dma_cleanup(host); |
| 442 | mmc_omap_reset_controller_fsm(host, SRD); | ||
| 443 | } | ||
| 421 | } | 444 | } |
| 422 | if ((status & DATA_TIMEOUT) || | 445 | if ((status & DATA_TIMEOUT) || |
| 423 | (status & DATA_CRC)) { | 446 | (status & DATA_CRC)) { |
| @@ -426,12 +449,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 426 | mmc_dma_cleanup(host); | 449 | mmc_dma_cleanup(host); |
| 427 | else | 450 | else |
| 428 | host->data->error = -EILSEQ; | 451 | host->data->error = -EILSEQ; |
| 429 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | 452 | mmc_omap_reset_controller_fsm(host, SRD); |
| 430 | OMAP_HSMMC_READ(host->base, | ||
| 431 | SYSCTL) | SRD); | ||
| 432 | while (OMAP_HSMMC_READ(host->base, | ||
| 433 | SYSCTL) & SRD) | ||
| 434 | ; | ||
| 435 | end_trans = 1; | 453 | end_trans = 1; |
| 436 | } | 454 | } |
| 437 | } | 455 | } |
| @@ -456,13 +474,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
| 456 | } | 474 | } |
| 457 | 475 | ||
| 458 | /* | 476 | /* |
| 459 | * Switch MMC operating voltage | 477 | * Switch MMC interface voltage ... only relevant for MMC1. |
| 478 | * | ||
| 479 | * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver. | ||
| 480 | * The MMC2 transceiver controls are used instead of DAT4..DAT7. | ||
| 481 | * Some chips, like eMMC ones, use internal transceivers. | ||
| 460 | */ | 482 | */ |
| 461 | static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) | 483 | static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) |
| 462 | { | 484 | { |
| 463 | u32 reg_val = 0; | 485 | u32 reg_val = 0; |
| 464 | int ret; | 486 | int ret; |
| 465 | 487 | ||
| 488 | if (host->id != OMAP_MMC1_DEVID) | ||
| 489 | return 0; | ||
| 490 | |||
| 466 | /* Disable the clocks */ | 491 | /* Disable the clocks */ |
| 467 | clk_disable(host->fclk); | 492 | clk_disable(host->fclk); |
| 468 | clk_disable(host->iclk); | 493 | clk_disable(host->iclk); |
| @@ -485,19 +510,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) | |||
| 485 | OMAP_HSMMC_WRITE(host->base, HCTL, | 510 | OMAP_HSMMC_WRITE(host->base, HCTL, |
| 486 | OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); | 511 | OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); |
| 487 | reg_val = OMAP_HSMMC_READ(host->base, HCTL); | 512 | reg_val = OMAP_HSMMC_READ(host->base, HCTL); |
| 513 | |||
| 488 | /* | 514 | /* |
| 489 | * If a MMC dual voltage card is detected, the set_ios fn calls | 515 | * If a MMC dual voltage card is detected, the set_ios fn calls |
| 490 | * this fn with VDD bit set for 1.8V. Upon card removal from the | 516 | * this fn with VDD bit set for 1.8V. Upon card removal from the |
| 491 | * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. | 517 | * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. |
| 492 | * | 518 | * |
| 493 | * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is | 519 | * Cope with a bit of slop in the range ... per data sheets: |
| 494 | * set in HCTL. | 520 | * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, |
| 521 | * but recommended values are 1.71V to 1.89V | ||
| 522 | * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max, | ||
| 523 | * but recommended values are 2.7V to 3.3V | ||
| 524 | * | ||
| 525 | * Board setup code shouldn't permit anything very out-of-range. | ||
| 526 | * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the | ||
| 527 | * middle range) but VSIM can't power DAT4..DAT7 at more than 3V. | ||
| 495 | */ | 528 | */ |
| 496 | if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || | 529 | if ((1 << vdd) <= MMC_VDD_23_24) |
| 497 | ((1 << vdd) == MMC_VDD_33_34))) | ||
| 498 | reg_val |= SDVS30; | ||
| 499 | if ((1 << vdd) == MMC_VDD_165_195) | ||
| 500 | reg_val |= SDVS18; | 530 | reg_val |= SDVS18; |
| 531 | else | ||
| 532 | reg_val |= SDVS30; | ||
| 501 | 533 | ||
| 502 | OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); | 534 | OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); |
| 503 | 535 | ||
| @@ -517,16 +549,15 @@ static void mmc_omap_detect(struct work_struct *work) | |||
| 517 | { | 549 | { |
| 518 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | 550 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, |
| 519 | mmc_carddetect_work); | 551 | mmc_carddetect_work); |
| 552 | struct omap_mmc_slot_data *slot = &mmc_slot(host); | ||
| 553 | |||
| 554 | host->carddetect = slot->card_detect(slot->card_detect_irq); | ||
| 520 | 555 | ||
| 521 | sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); | 556 | sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); |
| 522 | if (host->carddetect) { | 557 | if (host->carddetect) { |
| 523 | mmc_detect_change(host->mmc, (HZ * 200) / 1000); | 558 | mmc_detect_change(host->mmc, (HZ * 200) / 1000); |
| 524 | } else { | 559 | } else { |
| 525 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | 560 | mmc_omap_reset_controller_fsm(host, SRD); |
| 526 | OMAP_HSMMC_READ(host->base, SYSCTL) | SRD); | ||
| 527 | while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) | ||
| 528 | ; | ||
| 529 | |||
| 530 | mmc_detect_change(host->mmc, (HZ * 50) / 1000); | 561 | mmc_detect_change(host->mmc, (HZ * 50) / 1000); |
| 531 | } | 562 | } |
| 532 | } | 563 | } |
| @@ -538,7 +569,6 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) | |||
| 538 | { | 569 | { |
| 539 | struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; | 570 | struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; |
| 540 | 571 | ||
| 541 | host->carddetect = mmc_slot(host).card_detect(irq); | ||
| 542 | schedule_work(&host->mmc_carddetect_work); | 572 | schedule_work(&host->mmc_carddetect_work); |
| 543 | 573 | ||
| 544 | return IRQ_HANDLED; | 574 | return IRQ_HANDLED; |
| @@ -757,10 +787,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 757 | case MMC_POWER_OFF: | 787 | case MMC_POWER_OFF: |
| 758 | mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); | 788 | mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); |
| 759 | /* | 789 | /* |
| 760 | * Reset bus voltage to 3V if it got set to 1.8V earlier. | 790 | * Reset interface voltage to 3V if it's 1.8V now; |
| 791 | * only relevant on MMC-1, the others always use 1.8V. | ||
| 792 | * | ||
| 761 | * REVISIT: If we are able to detect cards after unplugging | 793 | * REVISIT: If we are able to detect cards after unplugging |
| 762 | * a 1.8V card, this code should not be needed. | 794 | * a 1.8V card, this code should not be needed. |
| 763 | */ | 795 | */ |
| 796 | if (host->id != OMAP_MMC1_DEVID) | ||
| 797 | break; | ||
| 764 | if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { | 798 | if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { |
| 765 | int vdd = fls(host->mmc->ocr_avail) - 1; | 799 | int vdd = fls(host->mmc->ocr_avail) - 1; |
| 766 | if (omap_mmc_switch_opcond(host, vdd) != 0) | 800 | if (omap_mmc_switch_opcond(host, vdd) != 0) |
| @@ -784,7 +818,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 784 | } | 818 | } |
| 785 | 819 | ||
| 786 | if (host->id == OMAP_MMC1_DEVID) { | 820 | if (host->id == OMAP_MMC1_DEVID) { |
| 787 | /* Only MMC1 can operate at 3V/1.8V */ | 821 | /* Only MMC1 can interface at 3V without some flavor |
| 822 | * of external transceiver; but they all handle 1.8V. | ||
| 823 | */ | ||
| 788 | if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && | 824 | if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && |
| 789 | (ios->vdd == DUAL_VOLT_OCR_BIT)) { | 825 | (ios->vdd == DUAL_VOLT_OCR_BIT)) { |
| 790 | /* | 826 | /* |
| @@ -1137,7 +1173,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) | |||
| 1137 | " level suspend\n"); | 1173 | " level suspend\n"); |
| 1138 | } | 1174 | } |
| 1139 | 1175 | ||
| 1140 | if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { | 1176 | if (host->id == OMAP_MMC1_DEVID |
| 1177 | && !(OMAP_HSMMC_READ(host->base, HCTL) | ||
| 1178 | & SDVSDET)) { | ||
| 1141 | OMAP_HSMMC_WRITE(host->base, HCTL, | 1179 | OMAP_HSMMC_WRITE(host->base, HCTL, |
| 1142 | OMAP_HSMMC_READ(host->base, HCTL) | 1180 | OMAP_HSMMC_READ(host->base, HCTL) |
| 1143 | & SDVSCLR); | 1181 | & SDVSCLR); |
