diff options
author | Loic Pallardy <loic.pallardy-ext@stericsson.com> | 2012-08-06 11:12:31 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-12-06 13:54:49 -0500 |
commit | 8d1e977da82ebd1defd44d085edc2c0140aecc2a (patch) | |
tree | f355ee13e20aab5ebcd123d68824ff24d4597ac2 /drivers/mmc/card | |
parent | 67c79db8d9c0e5d2e2075c9108f42566ce0f8a6f (diff) |
mmc: card: Add RPMB support in IOCTL interface
RPMB partition is accessing though /dev/block/mmcXrpmb device
User callers can read and write entire data frame(s) as defined
by JEDEC Standard JESD84-A441, using standard IOCTL interface.
Signed-off-by: Alex Macro <alex.macro@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Namjae Jeon <linkinjeon@gmail.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Krishna Konda <kkonda@codeaurora.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/card')
-rw-r--r-- | drivers/mmc/card/block.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1d341f3878ec..21056b9ef0a0 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c | |||
@@ -127,6 +127,10 @@ enum mmc_blk_status { | |||
127 | module_param(perdev_minors, int, 0444); | 127 | module_param(perdev_minors, int, 0444); |
128 | MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); | 128 | MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); |
129 | 129 | ||
130 | static inline int mmc_blk_part_switch(struct mmc_card *card, | ||
131 | struct mmc_blk_data *md); | ||
132 | static int get_card_status(struct mmc_card *card, u32 *status, int retries); | ||
133 | |||
130 | static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) | 134 | static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) |
131 | { | 135 | { |
132 | struct mmc_blk_data *md; | 136 | struct mmc_blk_data *md; |
@@ -358,6 +362,38 @@ out: | |||
358 | return ERR_PTR(err); | 362 | return ERR_PTR(err); |
359 | } | 363 | } |
360 | 364 | ||
365 | static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, | ||
366 | u32 retries_max) | ||
367 | { | ||
368 | int err; | ||
369 | u32 retry_count = 0; | ||
370 | |||
371 | if (!status || !retries_max) | ||
372 | return -EINVAL; | ||
373 | |||
374 | do { | ||
375 | err = get_card_status(card, status, 5); | ||
376 | if (err) | ||
377 | break; | ||
378 | |||
379 | if (!R1_STATUS(*status) && | ||
380 | (R1_CURRENT_STATE(*status) != R1_STATE_PRG)) | ||
381 | break; /* RPMB programming operation complete */ | ||
382 | |||
383 | /* | ||
384 | * Rechedule to give the MMC device a chance to continue | ||
385 | * processing the previous command without being polled too | ||
386 | * frequently. | ||
387 | */ | ||
388 | usleep_range(1000, 5000); | ||
389 | } while (++retry_count < retries_max); | ||
390 | |||
391 | if (retry_count == retries_max) | ||
392 | err = -EPERM; | ||
393 | |||
394 | return err; | ||
395 | } | ||
396 | |||
361 | static int mmc_blk_ioctl_cmd(struct block_device *bdev, | 397 | static int mmc_blk_ioctl_cmd(struct block_device *bdev, |
362 | struct mmc_ioc_cmd __user *ic_ptr) | 398 | struct mmc_ioc_cmd __user *ic_ptr) |
363 | { | 399 | { |
@@ -369,6 +405,8 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, | |||
369 | struct mmc_request mrq = {NULL}; | 405 | struct mmc_request mrq = {NULL}; |
370 | struct scatterlist sg; | 406 | struct scatterlist sg; |
371 | int err; | 407 | int err; |
408 | int is_rpmb = false; | ||
409 | u32 status = 0; | ||
372 | 410 | ||
373 | /* | 411 | /* |
374 | * The caller must have CAP_SYS_RAWIO, and must be calling this on the | 412 | * The caller must have CAP_SYS_RAWIO, and must be calling this on the |
@@ -388,6 +426,9 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, | |||
388 | goto cmd_err; | 426 | goto cmd_err; |
389 | } | 427 | } |
390 | 428 | ||
429 | if (md->area_type & MMC_BLK_DATA_AREA_RPMB) | ||
430 | is_rpmb = true; | ||
431 | |||
391 | card = md->queue.card; | 432 | card = md->queue.card; |
392 | if (IS_ERR(card)) { | 433 | if (IS_ERR(card)) { |
393 | err = PTR_ERR(card); | 434 | err = PTR_ERR(card); |
@@ -438,12 +479,23 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, | |||
438 | 479 | ||
439 | mmc_claim_host(card->host); | 480 | mmc_claim_host(card->host); |
440 | 481 | ||
482 | err = mmc_blk_part_switch(card, md); | ||
483 | if (err) | ||
484 | goto cmd_rel_host; | ||
485 | |||
441 | if (idata->ic.is_acmd) { | 486 | if (idata->ic.is_acmd) { |
442 | err = mmc_app_cmd(card->host, card); | 487 | err = mmc_app_cmd(card->host, card); |
443 | if (err) | 488 | if (err) |
444 | goto cmd_rel_host; | 489 | goto cmd_rel_host; |
445 | } | 490 | } |
446 | 491 | ||
492 | if (is_rpmb) { | ||
493 | err = mmc_set_blockcount(card, data.blocks, | ||
494 | idata->ic.write_flag & (1 << 31)); | ||
495 | if (err) | ||
496 | goto cmd_rel_host; | ||
497 | } | ||
498 | |||
447 | mmc_wait_for_req(card->host, &mrq); | 499 | mmc_wait_for_req(card->host, &mrq); |
448 | 500 | ||
449 | if (cmd.error) { | 501 | if (cmd.error) { |
@@ -479,6 +531,18 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, | |||
479 | } | 531 | } |
480 | } | 532 | } |
481 | 533 | ||
534 | if (is_rpmb) { | ||
535 | /* | ||
536 | * Ensure RPMB command has completed by polling CMD13 | ||
537 | * "Send Status". | ||
538 | */ | ||
539 | err = ioctl_rpmb_card_status_poll(card, &status, 5); | ||
540 | if (err) | ||
541 | dev_err(mmc_dev(card->host), | ||
542 | "%s: Card Status=0x%08X, error %d\n", | ||
543 | __func__, status, err); | ||
544 | } | ||
545 | |||
482 | cmd_rel_host: | 546 | cmd_rel_host: |
483 | mmc_release_host(card->host); | 547 | mmc_release_host(card->host); |
484 | 548 | ||