platform: msm_shared: Add ufs support for reading from rpmb partition

Add support in ufs to read from rpmb partition.

Change-Id: Ide41938a26cb58659ae5acbedffbe0a89bd874d4
diff --git a/platform/msm_shared/include/ucs.h b/platform/msm_shared/include/ucs.h
index e4d366e..58fb188 100644
--- a/platform/msm_shared/include/ucs.h
+++ b/platform/msm_shared/include/ucs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 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
@@ -40,6 +40,12 @@
 #define SCSI_SENSE_BUF_LEN             0x20
 #define SCSI_INQUIRY_LEN               36
 #define SCSI_CDB_PARAM_LEN             16
+#define SCSI_SEC_PROT                  0xEC
+#define SCSI_SEC_UFS_PROT_ID           0x0001
+
+#define RPMB_BLK_SIZE                  512
+#define RPMB_FRAME_SIZE                512
+#define RPMB_MIN_BLK_CNT               1
 
 /* FLAGS for indication of read or write */
 enum scsi_upiu_flags
@@ -87,6 +93,18 @@
 	uint32_t data_buffer_base;
 };
 
+struct scsi_sec_protocol_cdb
+{
+	uint8_t  opcode;
+	uint8_t  cdb1;
+	uint16_t sec_protocol_specific;
+	uint8_t  resv1;
+	uint8_t  resv2;
+	uint32_t alloc_tlen;
+	uint8_t  resv3;
+	uint8_t  control;
+}__PACKED;
+
 struct scsi_rdwr_cdb
 {
 	uint8_t  opcode;
@@ -148,4 +166,13 @@
 int ucs_do_scsi_read(struct ufs_dev *dev, struct scsi_rdwr_req *req);
 int ucs_do_scsi_write(struct ufs_dev *dev, struct scsi_rdwr_req *req);
 
+/*
+ * ucs_do_sci_rpmb_read function takes a RPMB frame, sector address and number of
+ * blocks to be read from RPMB partition as input and returns one or more RPMB
+ * frames as response along with total length of the reponse. The response is then
+ * processed by upper layers.
+ */
+int ucs_do_scsi_rpmb_read(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt,
+                                 uint32_t *resp_buffer, uint32_t *response_length);
+
 #endif
diff --git a/platform/msm_shared/ucs.c b/platform/msm_shared/ucs.c
index 1e03410..432f3a7 100644
--- a/platform/msm_shared/ucs.c
+++ b/platform/msm_shared/ucs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 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
@@ -80,6 +80,108 @@
 	return UFS_SUCCESS;
 }
 
+int ucs_do_scsi_rpmb_read(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt,
+                                 uint32_t *resp_buf, uint32_t *resp_len)
+{
+	// validate input parameters
+	ASSERT(req_buf);
+	ASSERT(resp_buf);
+	ASSERT(resp_len);
+
+	STACKBUF_DMA_ALIGN(cdb, sizeof(struct scsi_sec_protocol_cdb));
+	struct scsi_req_build_type   req_upiu;
+	struct scsi_sec_protocol_cdb *cdb_out_param, *cdb_in_param;
+	uint32_t                     blks_remaining;
+	uint32_t                     blks_to_transfer;
+	uint64_t                     bytes_to_transfer;
+	uint32_t                     start_blk;
+	uint64_t                     max_size;
+	blks_remaining    = blk_cnt;
+	blks_to_transfer  = blks_remaining;
+	bytes_to_transfer = blks_to_transfer * RPMB_FRAME_SIZE;
+
+	// check if total bytes to transfer exceed max supported size
+	max_size = dev->rpmb_rw_size * RPMB_FRAME_SIZE * blk_cnt;
+	if (bytes_to_transfer > max_size)
+	{
+		dprintf(CRITICAL, "RPMB request transfer size %llu greater than max transfer size %llu\n", bytes_to_transfer, max_size);
+		return -UFS_FAILURE;
+	}
+#ifdef DEBUG_UFS
+	dprintf(INFO, "rpmb_read: req_buf: 0x%x blk_count: 0x%x\n", *req_buf, blk_cnt);
+	dprintf(INFO, "rpmb_read: bytes_to_transfer: 0x%x blks_to_transfer: 0x%x\n",
+                   bytes_to_transfer, blks_to_transfer);
+#endif
+	// send the request
+	cdb_out_param = (struct scsi_sec_protocol_cdb*) cdb;
+	memset(cdb_out_param, 0, sizeof(struct scsi_sec_protocol_cdb));
+
+	cdb_out_param->opcode                = SCSI_CMD_SECPROT_OUT;
+	cdb_out_param->cdb1                  = SCSI_SEC_PROT;
+	cdb_out_param->sec_protocol_specific = BE16(SCSI_SEC_UFS_PROT_ID);
+	cdb_out_param->alloc_tlen            = BE32(bytes_to_transfer);
+
+	// Flush CDB to memory
+	dsb();
+	arch_clean_invalidate_cache_range((addr_t) cdb_out_param, sizeof(struct scsi_sec_protocol_cdb));
+
+	memset(&req_upiu, 0, sizeof(struct scsi_req_build_type));
+
+	req_upiu.cdb              = (addr_t) cdb_out_param;
+	req_upiu.data_buffer_addr = req_buf;
+	req_upiu.data_len         = bytes_to_transfer;
+	req_upiu.flags            = UPIU_FLAGS_WRITE;
+	req_upiu.lun              = UFS_WLUN_RPMB;
+	req_upiu.dd               = UTRD_TARGET_TO_SYSTEM;
+
+#ifdef DEBUG_UFS
+	dprintf(INFO, "Sending RPMB Read request\n");
+#endif
+	if (ucs_do_scsi_cmd(dev, &req_upiu))
+	{
+		dprintf(CRITICAL, "%s:%d ucs_do_scsi_rpmb_read: failed\n", __func__, __LINE__);
+		return -UFS_FAILURE;
+	}
+#ifdef DEBUG_UFS
+	dprintf(INFO, "Sending RPMB Read request complete\n");
+#endif
+	// read the response
+	cdb_in_param = (struct scsi_sec_protocol_cdb*) cdb;
+	memset(cdb_in_param, 0, sizeof(struct scsi_sec_protocol_cdb));
+
+	cdb_in_param->opcode                = SCSI_CMD_SECPROT_IN;
+	cdb_in_param->cdb1                  = SCSI_SEC_PROT;
+	cdb_in_param->sec_protocol_specific = BE16(SCSI_SEC_UFS_PROT_ID);
+	cdb_in_param->alloc_tlen            = BE32(bytes_to_transfer);
+
+	// Flush CDB to memory
+	dsb();
+	arch_clean_invalidate_cache_range((addr_t) cdb_in_param, sizeof(struct scsi_sec_protocol_cdb));
+
+	memset(&req_upiu, 0, sizeof(struct scsi_req_build_type));
+
+	req_upiu.cdb              = (addr_t) cdb_in_param;
+	req_upiu.data_buffer_addr = resp_buf;
+	req_upiu.data_len         = bytes_to_transfer;
+	req_upiu.flags            = UPIU_FLAGS_READ;
+	req_upiu.lun              = UFS_WLUN_RPMB;
+	req_upiu.dd               = UTRD_SYSTEM_TO_TARGET;
+
+#ifdef DEBUG_UFS
+	dprintf(INFO, "Sending RPMB Read response\n");
+#endif
+	if (ucs_do_scsi_cmd(dev, &req_upiu))
+	{
+		dprintf(CRITICAL, "%s:%d ucs_do_scsi_rpmb_read: failed\n", __func__, __LINE__);
+		return -UFS_FAILURE;
+	}
+#ifdef DEBUG_UFS
+	dprintf(SPEW, "Sending RPMB Read response complete\n");
+#endif
+	*resp_len = bytes_to_transfer;
+	return UFS_SUCCESS;
+}
+
 int ucs_do_scsi_read(struct ufs_dev *dev, struct scsi_rdwr_req *req)
 {
 	STACKBUF_DMA_ALIGN(cdb, sizeof(struct scsi_rdwr_cdb));