platform: msm_shared: Add support for rpmb
Add support to perform read and write from rpmb partition in
emmc driver.
Change-Id: I41ded88d079973dbc6dddd5bfcd69a9d9bb8a3b4
diff --git a/platform/msm_shared/include/mmc_sdhci.h b/platform/msm_shared/include/mmc_sdhci.h
index 1612e61..95d636e 100644
--- a/platform/msm_shared/include/mmc_sdhci.h
+++ b/platform/msm_shared/include/mmc_sdhci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -101,6 +101,7 @@
#define MMC_USR_WP 171
#define MMC_ERASE_TIMEOUT_MULT 223
#define MMC_HC_ERASE_GRP_SIZE 224
+#define MMC_PARTITION_CONFIG 179
/* Values for ext csd fields */
#define MMC_HS_TIMING 0x1
@@ -117,6 +118,13 @@
#define MMC_HC_ERASE_MULT (512 * 1024)
#define RST_N_FUNC_ENABLE BIT(0)
+/* RPMB Related */
+#define RPMB_PART_MIN_SIZE (128 * 2014)
+#define RPMB_SIZE_MULT 168
+#define REL_WR_SEC_C 222
+#define PARTITION_ACCESS_MASK 0x7
+#define MAX_RPMB_CMDS 0x3
+
/* Command related */
#define MMC_MAX_COMMAND_RETRY 1000
#define MMC_MAX_CARD_STAT_RETRY 10000
@@ -226,6 +234,12 @@
#define MMC_CARD_MMC(card) ((card->type == MMC_TYPE_STD_MMC) || \
(card->type == MMC_TYPE_MMCHC))
+enum part_access_type
+{
+ PART_ACCESS_DEFAULT = 0x0,
+ PART_ACCESS_RPMB = 0x3,
+};
+
/* CSD Register.
* Note: not all the fields have been defined here
*/
@@ -292,6 +306,8 @@
uint8_t *ext_csd; /* Ext CSD for the card info */
uint32_t raw_csd[4]; /* Raw CSD for the card */
uint32_t raw_scr[2]; /* SCR for SD card */
+ uint32_t rpmb_size; /* Size of rpmb partition */
+ uint32_t rel_wr_count; /* Reliable write count */
struct mmc_cid cid; /* CID structure */
struct mmc_csd csd; /* CSD structure */
struct mmc_sd_scr scr; /* SCR structure */
@@ -337,4 +353,6 @@
void mmc_put_card_to_sleep(struct mmc_device *dev);
/* API: Change the driver type of the card */
bool mmc_set_drv_type(struct sdhci_host *host, struct mmc_card *card, uint8_t drv_type);
+/* API: Send the read & write command sequence to rpmb */
+uint32_t mmc_sdhci_rpmb_send(struct mmc_device *dev, struct mmc_command *cmd);
#endif
diff --git a/platform/msm_shared/include/sdhci.h b/platform/msm_shared/include/sdhci.h
index 604ac81..c0b484d 100644
--- a/platform/msm_shared/include/sdhci.h
+++ b/platform/msm_shared/include/sdhci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -100,6 +100,7 @@
uint32_t cmd_retry; /* Retry the command, if card is busy */
uint32_t cmd23_support; /* If card supports cmd23 */
uint64_t cmd_timeout; /* Command timeout in us */
+ bool write_flag; /* Write flag, for reliable write cases */
struct mmc_data data; /* Data pointer */
};
diff --git a/platform/msm_shared/mmc_sdhci.c b/platform/msm_shared/mmc_sdhci.c
index 2632961..3bc7110 100644
--- a/platform/msm_shared/mmc_sdhci.c
+++ b/platform/msm_shared/mmc_sdhci.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -240,6 +240,9 @@
card->wp_grp_size = (card->csd.wp_grp_size + 1) * (card->csd.erase_grp_size + 1) \
* (card->csd.erase_grp_mult + 1);
+ card->rpmb_size = RPMB_PART_MIN_SIZE * card->ext_csd[RPMB_SIZE_MULT];
+ card->rel_wr_count = card->ext_csd[REL_WR_SEC_C];
+
dprintf(SPEW, "Decoded CSD fields:\n");
dprintf(SPEW, "cmmc_structure: %u\n", mmc_csd.cmmc_structure);
dprintf(SPEW, "card_cmd_class: %x\n", mmc_csd.card_cmd_class);
@@ -2366,3 +2369,107 @@
if(sdhci_send_command(&dev->host, &cmd))
dprintf(CRITICAL, "card sleep error: %s\n", __func__);
}
+
+/*
+ * Switch the partition access type to rpmb or default
+ */
+static uint32_t mmc_sdhci_switch_part(struct mmc_device *dev, uint32_t type)
+{
+ uint32_t part_access;
+ uint32_t ret;
+
+ /* Clear the partition access */
+ part_access = dev->card.ext_csd[MMC_PARTITION_CONFIG] & ~PARTITION_ACCESS_MASK;
+ part_access |= type;
+
+ ret = mmc_switch_cmd(&dev->host, &dev->card, MMC_ACCESS_WRITE, MMC_PARTITION_CONFIG, part_access);
+
+ if (ret)
+ {
+ dprintf(CRITICAL, "Failed to switch partition to type: %u\n", type);
+ return 1;
+ }
+
+ dev->card.ext_csd[MMC_PARTITION_CONFIG] = part_access;
+ return 0;
+}
+
+static uint32_t mmc_sdhci_set_blk_cnt(struct mmc_device *dev, uint32_t blk_cnt, uint32_t rel_write)
+{
+ struct mmc_command cmd = {0};
+
+ cmd.cmd_index = CMD23_SET_BLOCK_COUNT;
+ cmd.argument = blk_cnt & 0x0000ffff;
+ cmd.argument |= rel_write;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1;
+
+ if (sdhci_send_command(&dev->host, &cmd))
+ {
+ dprintf(CRITICAL, "Set block count failed: %s\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+uint32_t mmc_sdhci_rpmb_send(struct mmc_device *dev, struct mmc_command *cmd)
+{
+ int i;
+ uint32_t retry = 5;
+ uint32_t status;
+ uint32_t rel_write = 0;
+ uint32_t ret = 1;
+
+ ASSERT(cmd);
+
+ /* 1. Set the partition type to rpmb */
+ if (mmc_sdhci_switch_part(dev, PART_ACCESS_RPMB))
+ return 1;
+
+ for (i = 0; i < MAX_RPMB_CMDS; i++)
+ {
+ if (!cmd[i].cmd_index)
+ break;
+
+ if (cmd[i].write_flag == true)
+ rel_write = BIT(31);
+ else
+ rel_write = 0;
+
+ /* 2. Set the block count using cmd23 */
+ if (mmc_sdhci_set_blk_cnt(dev, cmd[i].data.num_blocks, rel_write))
+ goto err;
+
+ /* 3. Send the command */
+ if (sdhci_send_command(&dev->host, &cmd[i]))
+ goto err;
+ do
+ {
+ /* 4. Poll for card status to ensure rpmb operation completeness */
+ if (mmc_get_card_status(&dev->host, &dev->card, &status))
+ {
+ dprintf(CRITICAL, "Failed to get card status after rpmb operations\n");
+ goto err;
+ }
+
+ retry--;
+ udelay(500);
+ if (!retry)
+ {
+ dprintf(CRITICAL, "Card status check timed out after rpmb operations\n");
+ goto err;
+ }
+ } while(!(status & MMC_READY_FOR_DATA) || (MMC_CARD_STATUS(status) == MMC_PROG_STATE));
+ }
+
+ /* If we reach here, that means success */
+ ret = 0;
+
+err:
+ /* 5. Switch the partition back to default type */
+ if (mmc_sdhci_switch_part(dev, PART_ACCESS_DEFAULT))
+ ret = 1;
+
+ return ret;
+}