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 | ||