diff options
| author | Jaehoon Chung <jh80.chung@samsung.com> | 2011-10-18 01:26:42 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2011-10-26 16:32:29 -0400 |
| commit | eb0d8f135b6730d6d0324a064664d121334290e7 (patch) | |
| tree | 74876f5d20163bb5e9b185d9357237e1f22a2262 /drivers/mmc | |
| parent | 881d1c25f765938a95def5afe39486ce39f9fc96 (diff) | |
mmc: core: support HPI send command
HPI command is defined in eMMC4.41.
This feature is important for eMMC4.5 devices.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/core/core.c | 57 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc.c | 31 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc_ops.c | 31 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 |
4 files changed, 120 insertions, 0 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 235bb6a1f973..fe65bb377e25 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
| @@ -380,6 +380,63 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) | |||
| 380 | EXPORT_SYMBOL(mmc_wait_for_req); | 380 | EXPORT_SYMBOL(mmc_wait_for_req); |
| 381 | 381 | ||
| 382 | /** | 382 | /** |
| 383 | * mmc_interrupt_hpi - Issue for High priority Interrupt | ||
| 384 | * @card: the MMC card associated with the HPI transfer | ||
| 385 | * | ||
| 386 | * Issued High Priority Interrupt, and check for card status | ||
| 387 | * util out-of prg-state. | ||
| 388 | */ | ||
| 389 | int mmc_interrupt_hpi(struct mmc_card *card) | ||
| 390 | { | ||
| 391 | int err; | ||
| 392 | u32 status; | ||
| 393 | |||
| 394 | BUG_ON(!card); | ||
| 395 | |||
| 396 | if (!card->ext_csd.hpi_en) { | ||
| 397 | pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host)); | ||
| 398 | return 1; | ||
| 399 | } | ||
| 400 | |||
| 401 | mmc_claim_host(card->host); | ||
| 402 | err = mmc_send_status(card, &status); | ||
| 403 | if (err) { | ||
| 404 | pr_err("%s: Get card status fail\n", mmc_hostname(card->host)); | ||
| 405 | goto out; | ||
| 406 | } | ||
| 407 | |||
| 408 | /* | ||
| 409 | * If the card status is in PRG-state, we can send the HPI command. | ||
| 410 | */ | ||
| 411 | if (R1_CURRENT_STATE(status) == R1_STATE_PRG) { | ||
| 412 | do { | ||
| 413 | /* | ||
| 414 | * We don't know when the HPI command will finish | ||
| 415 | * processing, so we need to resend HPI until out | ||
| 416 | * of prg-state, and keep checking the card status | ||
| 417 | * with SEND_STATUS. If a timeout error occurs when | ||
| 418 | * sending the HPI command, we are already out of | ||
| 419 | * prg-state. | ||
| 420 | */ | ||
| 421 | err = mmc_send_hpi_cmd(card, &status); | ||
| 422 | if (err) | ||
| 423 | pr_debug("%s: abort HPI (%d error)\n", | ||
| 424 | mmc_hostname(card->host), err); | ||
| 425 | |||
| 426 | err = mmc_send_status(card, &status); | ||
| 427 | if (err) | ||
| 428 | break; | ||
| 429 | } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); | ||
| 430 | } else | ||
| 431 | pr_debug("%s: Left prg-state\n", mmc_hostname(card->host)); | ||
| 432 | |||
| 433 | out: | ||
| 434 | mmc_release_host(card->host); | ||
| 435 | return err; | ||
| 436 | } | ||
| 437 | EXPORT_SYMBOL(mmc_interrupt_hpi); | ||
| 438 | |||
| 439 | /** | ||
| 383 | * mmc_wait_for_cmd - start a command and wait for completion | 440 | * mmc_wait_for_cmd - start a command and wait for completion |
| 384 | * @host: MMC host to start command | 441 | * @host: MMC host to start command |
| 385 | * @cmd: MMC command to start | 442 | * @cmd: MMC command to start |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index de5900aa81cd..fb5bf01dd1b2 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
| @@ -448,6 +448,21 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
| 448 | } | 448 | } |
| 449 | 449 | ||
| 450 | if (card->ext_csd.rev >= 5) { | 450 | if (card->ext_csd.rev >= 5) { |
| 451 | /* check whether the eMMC card supports HPI */ | ||
| 452 | if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { | ||
| 453 | card->ext_csd.hpi = 1; | ||
| 454 | if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2) | ||
| 455 | card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION; | ||
| 456 | else | ||
| 457 | card->ext_csd.hpi_cmd = MMC_SEND_STATUS; | ||
| 458 | /* | ||
| 459 | * Indicate the maximum timeout to close | ||
| 460 | * a command interrupted by HPI | ||
| 461 | */ | ||
| 462 | card->ext_csd.out_of_int_time = | ||
| 463 | ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10; | ||
| 464 | } | ||
| 465 | |||
| 451 | card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; | 466 | card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; |
| 452 | card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION]; | 467 | card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION]; |
| 453 | } | 468 | } |
| @@ -896,6 +911,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
| 896 | } | 911 | } |
| 897 | 912 | ||
| 898 | /* | 913 | /* |
| 914 | * Enable HPI feature (if supported) | ||
| 915 | */ | ||
| 916 | if (card->ext_csd.hpi) { | ||
| 917 | err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||
| 918 | EXT_CSD_HPI_MGMT, 1, 0); | ||
| 919 | if (err && err != -EBADMSG) | ||
| 920 | goto free_card; | ||
| 921 | if (err) { | ||
| 922 | pr_warning("%s: Enabling HPI failed\n", | ||
| 923 | mmc_hostname(card->host)); | ||
| 924 | err = 0; | ||
| 925 | } else | ||
| 926 | card->ext_csd.hpi_en = 1; | ||
| 927 | } | ||
| 928 | |||
| 929 | /* | ||
| 899 | * Compute bus speed. | 930 | * Compute bus speed. |
| 900 | */ | 931 | */ |
| 901 | max_dtr = (unsigned int)-1; | 932 | max_dtr = (unsigned int)-1; |
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 4e11d56b3f70..007863eea4fb 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c | |||
| @@ -547,3 +547,34 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width) | |||
| 547 | err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); | 547 | err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); |
| 548 | return err; | 548 | return err; |
| 549 | } | 549 | } |
| 550 | |||
| 551 | int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) | ||
| 552 | { | ||
| 553 | struct mmc_command cmd = {0}; | ||
| 554 | unsigned int opcode; | ||
| 555 | unsigned int flags; | ||
| 556 | int err; | ||
| 557 | |||
| 558 | opcode = card->ext_csd.hpi_cmd; | ||
| 559 | if (opcode == MMC_STOP_TRANSMISSION) | ||
| 560 | flags = MMC_RSP_R1 | MMC_CMD_AC; | ||
| 561 | else if (opcode == MMC_SEND_STATUS) | ||
| 562 | flags = MMC_RSP_R1 | MMC_CMD_AC; | ||
| 563 | |||
| 564 | cmd.opcode = opcode; | ||
| 565 | cmd.arg = card->rca << 16 | 1; | ||
| 566 | cmd.flags = flags; | ||
| 567 | cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time; | ||
| 568 | |||
| 569 | err = mmc_wait_for_cmd(card->host, &cmd, 0); | ||
| 570 | if (err) { | ||
| 571 | pr_warn("%s: error %d interrupting operation. " | ||
| 572 | "HPI command response %#x\n", mmc_hostname(card->host), | ||
| 573 | err, cmd.resp[0]); | ||
| 574 | return err; | ||
| 575 | } | ||
| 576 | if (status) | ||
| 577 | *status = cmd.resp[0]; | ||
| 578 | |||
| 579 | return 0; | ||
| 580 | } | ||
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 9276946fa5b7..3dd8941c2980 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h | |||
| @@ -26,6 +26,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); | |||
| 26 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); | 26 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); |
| 27 | int mmc_card_sleepawake(struct mmc_host *host, int sleep); | 27 | int mmc_card_sleepawake(struct mmc_host *host, int sleep); |
| 28 | int mmc_bus_test(struct mmc_card *card, u8 bus_width); | 28 | int mmc_bus_test(struct mmc_card *card, u8 bus_width); |
| 29 | int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); | ||
| 29 | 30 | ||
| 30 | #endif | 31 | #endif |
| 31 | 32 | ||
