diff options
| -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 | ||||
| -rw-r--r-- | include/linux/mmc/card.h | 4 | ||||
| -rw-r--r-- | include/linux/mmc/core.h | 1 | ||||
| -rw-r--r-- | include/linux/mmc/mmc.h | 3 |
7 files changed, 128 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 | ||
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 32492b78aed6..1684d92a8015 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h | |||
| @@ -69,10 +69,14 @@ struct mmc_ext_csd { | |||
| 69 | unsigned long long enhanced_area_offset; /* Units: Byte */ | 69 | unsigned long long enhanced_area_offset; /* Units: Byte */ |
| 70 | unsigned int enhanced_area_size; /* Units: KB */ | 70 | unsigned int enhanced_area_size; /* Units: KB */ |
| 71 | unsigned int cache_size; /* Units: KB */ | 71 | unsigned int cache_size; /* Units: KB */ |
| 72 | bool hpi_en; /* HPI enablebit */ | ||
| 73 | bool hpi; /* HPI support bit */ | ||
| 74 | unsigned int hpi_cmd; /* cmd used as HPI */ | ||
| 72 | u8 raw_partition_support; /* 160 */ | 75 | u8 raw_partition_support; /* 160 */ |
| 73 | u8 raw_erased_mem_count; /* 181 */ | 76 | u8 raw_erased_mem_count; /* 181 */ |
| 74 | u8 raw_ext_csd_structure; /* 194 */ | 77 | u8 raw_ext_csd_structure; /* 194 */ |
| 75 | u8 raw_card_type; /* 196 */ | 78 | u8 raw_card_type; /* 196 */ |
| 79 | u8 out_of_int_time; /* 198 */ | ||
| 76 | u8 raw_s_a_timeout; /* 217 */ | 80 | u8 raw_s_a_timeout; /* 217 */ |
| 77 | u8 raw_hc_erase_gap_size; /* 221 */ | 81 | u8 raw_hc_erase_gap_size; /* 221 */ |
| 78 | u8 raw_erase_timeout_mult; /* 223 */ | 82 | u8 raw_erase_timeout_mult; /* 223 */ |
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 67bac374d122..174a844a5dda 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h | |||
| @@ -136,6 +136,7 @@ struct mmc_async_req; | |||
| 136 | 136 | ||
| 137 | extern struct mmc_async_req *mmc_start_req(struct mmc_host *, | 137 | extern struct mmc_async_req *mmc_start_req(struct mmc_host *, |
| 138 | struct mmc_async_req *, int *); | 138 | struct mmc_async_req *, int *); |
| 139 | extern int mmc_interrupt_hpi(struct mmc_card *); | ||
| 139 | extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); | 140 | extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); |
| 140 | extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); | 141 | extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); |
| 141 | extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); | 142 | extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); |
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 160e4c5bdae0..0e7135697d11 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h | |||
| @@ -276,6 +276,7 @@ struct _mmc_csd { | |||
| 276 | #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ | 276 | #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ |
| 277 | #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ | 277 | #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ |
| 278 | #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ | 278 | #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ |
| 279 | #define EXT_CSD_HPI_MGMT 161 /* R/W */ | ||
| 279 | #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ | 280 | #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ |
| 280 | #define EXT_CSD_SANITIZE_START 165 /* W */ | 281 | #define EXT_CSD_SANITIZE_START 165 /* W */ |
| 281 | #define EXT_CSD_WR_REL_PARAM 166 /* RO */ | 282 | #define EXT_CSD_WR_REL_PARAM 166 /* RO */ |
| @@ -288,6 +289,7 @@ struct _mmc_csd { | |||
| 288 | #define EXT_CSD_REV 192 /* RO */ | 289 | #define EXT_CSD_REV 192 /* RO */ |
| 289 | #define EXT_CSD_STRUCTURE 194 /* RO */ | 290 | #define EXT_CSD_STRUCTURE 194 /* RO */ |
| 290 | #define EXT_CSD_CARD_TYPE 196 /* RO */ | 291 | #define EXT_CSD_CARD_TYPE 196 /* RO */ |
| 292 | #define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */ | ||
| 291 | #define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ | 293 | #define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ |
| 292 | #define EXT_CSD_PWR_CL_52_195 200 /* RO */ | 294 | #define EXT_CSD_PWR_CL_52_195 200 /* RO */ |
| 293 | #define EXT_CSD_PWR_CL_26_195 201 /* RO */ | 295 | #define EXT_CSD_PWR_CL_26_195 201 /* RO */ |
| @@ -311,6 +313,7 @@ struct _mmc_csd { | |||
| 311 | #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ | 313 | #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ |
| 312 | #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ | 314 | #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ |
| 313 | #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ | 315 | #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ |
| 316 | #define EXT_CSD_HPI_FEATURES 503 /* RO */ | ||
| 314 | 317 | ||
| 315 | /* | 318 | /* |
| 316 | * EXT_CSD field definitions | 319 | * EXT_CSD field definitions |
