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