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;
+}