Merge "platform: msm_shared: RPMB Write performance enhancement"
diff --git a/platform/msm_shared/include/mmc_sdhci.h b/platform/msm_shared/include/mmc_sdhci.h
index 6ef404b..54f09ee 100644
--- a/platform/msm_shared/include/mmc_sdhci.h
+++ b/platform/msm_shared/include/mmc_sdhci.h
@@ -89,6 +89,7 @@
#define MMC_EXT_CSD_RST_N_FUNC 162
#define MMC_EXT_MMC_BUS_WIDTH 183
#define MMC_EXT_MMC_HS_TIMING 185
+#define MMC_EXT_CSD_REV 192
#define MMC_DEVICE_TYPE 196
#define MMC_EXT_MMC_DRV_STRENGTH 197
#define MMC_EXT_HC_WP_GRP_SIZE 221
@@ -102,6 +103,7 @@
#define MMC_ERASE_TIMEOUT_MULT 223
#define MMC_HC_ERASE_GRP_SIZE 224
#define MMC_PARTITION_CONFIG 179
+#define MMC_EXT_CSD_EN_RPMB_REL_WR 166 //emmc 5.1 and above
/* Values for ext csd fields */
#define MMC_HS_TIMING 0x1
diff --git a/platform/msm_shared/include/rpmb.h b/platform/msm_shared/include/rpmb.h
index 6cd7ecd..f954dfb 100644
--- a/platform/msm_shared/include/rpmb.h
+++ b/platform/msm_shared/include/rpmb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-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
@@ -147,15 +147,15 @@
int write_device_info_rpmb(void *info, uint32_t sz);
/* Function Prototypes */
-int rpmb_write_emmc(struct mmc_device *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
+int rpmb_write_emmc(struct mmc_device *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t rel_wr_count, uint32_t *resp_buf, uint32_t *resp_len);
int rpmb_read_emmc(struct mmc_device *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
-int rpmb_write_ufs(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
+int rpmb_write_ufs(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t rel_wr_count, uint32_t *resp_buf, uint32_t *resp_len);
int rpmb_read_ufs(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
/* APIs exposed to applications */
int rpmb_init();
int rpmb_uninit();
-int rpmb_write(uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
+int rpmb_write(uint32_t *req_buf, uint32_t blk_cnt, uint32_t rel_wr_count, uint32_t *resp_buf, uint32_t *resp_len);
int rpmb_read(uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len);
struct rpmb_init_info *rpmb_get_init_info();
int rpmb_get_app_handle();
diff --git a/platform/msm_shared/include/sdhci.h b/platform/msm_shared/include/sdhci.h
index c0b484d..64fb2b0 100644
--- a/platform/msm_shared/include/sdhci.h
+++ b/platform/msm_shared/include/sdhci.h
@@ -102,6 +102,7 @@
uint64_t cmd_timeout; /* Command timeout in us */
bool write_flag; /* Write flag, for reliable write cases */
struct mmc_data data; /* Data pointer */
+ uint8_t rel_write; /* Reliable write enable flag */
};
/*
diff --git a/platform/msm_shared/rpmb/rpmb.c b/platform/msm_shared/rpmb/rpmb.c
index 36bacab..29d72e3 100644
--- a/platform/msm_shared/rpmb/rpmb.c
+++ b/platform/msm_shared/rpmb/rpmb.c
@@ -50,7 +50,30 @@
{
struct mmc_device *mmc_dev = (struct mmc_device *) dev;
info.size = mmc_dev->card.rpmb_size / RPMB_MIN_BLK_SZ;
- info.rel_wr_count = mmc_dev->card.rel_wr_count;
+ if (mmc_dev->card.ext_csd[MMC_EXT_CSD_REV] < 8)
+ {
+ dprintf(SPEW, "EMMC Version < 5.1\n");
+ info.rel_wr_count = mmc_dev->card.rel_wr_count;
+ }
+ else
+ {
+ if (mmc_dev->card.ext_csd[MMC_EXT_CSD_EN_RPMB_REL_WR] == 0)
+ {
+ dprintf(SPEW, "EMMC Version >= 5.1 EN_RPMB_REL_WR = 0\n");
+ // according to emmc version 5.1 and above if EN_RPMB_REL_WR in extended
+ // csd is not set the maximum number of frames that can be reliably written
+ // to emmc would be 2
+ info.rel_wr_count = 2;
+ }
+ else
+ {
+ dprintf(SPEW, "EMMC Version >= 5.1 EN_RPMB_REL_WR = 1\n");
+ // according to emmc version 5.1 and above if EN_RPMB_REL_WR in extended
+ // csd is set the maximum number of frames that can be reliably written
+ // to emmc would be 32
+ info.rel_wr_count = 32;
+ }
+ }
info.dev_type = EMMC_RPMB;
}
else
@@ -105,12 +128,12 @@
return rpmb_read_ufs(dev, req, req_len, resp, resp_len);
}
-int rpmb_write(uint32_t *req, uint32_t req_len, uint32_t *resp, uint32_t *resp_len)
+int rpmb_write(uint32_t *req, uint32_t req_len, uint32_t rel_wr_count, uint32_t *resp, uint32_t *resp_len)
{
if (platform_boot_dev_isemmc())
- return rpmb_write_emmc(dev, req, req_len, resp, resp_len);
+ return rpmb_write_emmc(dev, req, req_len, rel_wr_count, resp, resp_len);
else
- return rpmb_write_ufs(dev, req, req_len, resp, resp_len);
+ return rpmb_write_ufs(dev, req, req_len, rel_wr_count, resp, resp_len);
}
/* This API calls into TZ app to read device_info */
diff --git a/platform/msm_shared/rpmb/rpmb_emmc.c b/platform/msm_shared/rpmb/rpmb_emmc.c
index 2bfa545..3b00e09 100644
--- a/platform/msm_shared/rpmb/rpmb_emmc.c
+++ b/platform/msm_shared/rpmb/rpmb_emmc.c
@@ -47,17 +47,36 @@
.requestresponse[1] = READ_RESULT_FLAG,
};
-int rpmb_write_emmc(struct mmc_device *dev,uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len)
+int rpmb_write_emmc(struct mmc_device *dev,uint32_t *req_buf, uint32_t blk_cnt, uint32_t rel_wr_count, uint32_t *resp_buf, uint32_t *resp_len)
{
- uint32_t i;
+ uint32_t i, num_trans;
int ret = 0;
struct mmc_command cmd[3] = {{0},{0},{0}};
struct rpmb_frame *result = (struct rpmb_frame *)resp_buf;
- for (i = 0; i < blk_cnt; i++)
+ dprintf(INFO, "rpmb write command called with blk_cnt: 0x%x\n", blk_cnt);
+ if (rel_wr_count == 0x2)
+ {
+ // if reliable write count reported by secure app is 2 then we can
+ // send two half sectors in one transaction. So overall number of
+ // transactions would be total block count by 2.
+ num_trans = blk_cnt / 2;
+ }
+ else if(rel_wr_count == 0x1)
+ {
+ num_trans = blk_cnt; // rel_rw_count = 1 for emmc 5.0 and below
+ }
+ else
+ {
+ dprintf(CRITICAL, "Reliable write count > 2 is not supported\n");
+ return -1;
+ }
+
+ for (i = 0; i < num_trans; i++)
{
#if DEBUG_RPMB
- dump_rpmb_frame((uint8_t *)req_buf, "request");
+ for(uint8_t j = 0; j < rel_wr_count; j++)
+ dump_rpmb_frame((uint8_t *)req_buf + (j * 512), "request");
#endif
/* CMD25 program data packet */
@@ -69,8 +88,16 @@
cmd[0].trans_mode = SDHCI_MMC_WRITE;
cmd[0].data_present = 0x1;
cmd[0].data.data_ptr = (void *)req_buf;
- cmd[0].data.num_blocks = RPMB_MIN_BLK_CNT;
-
+ if (rel_wr_count == 0x1)
+ {
+ cmd[0].rel_write = 0;
+ cmd[0].data.num_blocks = RPMB_MIN_BLK_CNT;
+ }
+ else if(rel_wr_count == 0x2)
+ {
+ cmd[0].rel_write = 1;
+ cmd[0].data.num_blocks = 0x2;
+ }
/* CMD25 Result Register Read Request Packet */
cmd[1].write_flag = false;
cmd[1].cmd_index = CMD25_WRITE_MULTIPLE_BLOCK;
@@ -101,6 +128,9 @@
break;
}
+#if DEBUG_RPMB
+ dump_rpmb_frame((uint8_t *)resp_buf, "response");
+#endif
if (result->result[0] == 0x80)
{
dprintf(CRITICAL, "Max write counter reached: \n");
@@ -112,12 +142,11 @@
dprintf(CRITICAL, "%s\n", str_err[result->result[1]]);
break;
}
+ if (rel_wr_count == 0x1)
+ req_buf = (uint32_t*) ((uint8_t*)req_buf + (RPMB_BLK_SIZE));
+ else if(rel_wr_count == 0x2)
+ req_buf = (uint32_t*) ((uint8_t*)req_buf + (RPMB_BLK_SIZE * 0x2));
- req_buf = (uint32_t*) ((uint8_t*)req_buf + RPMB_BLK_SIZE);
-
-#if DEBUG_RPMB
- dump_rpmb_frame((uint8_t *)resp_buf, "response");
-#endif
}
*resp_len = RPMB_MIN_BLK_CNT * RPMB_BLK_SIZE;
diff --git a/platform/msm_shared/rpmb/rpmb_listener.c b/platform/msm_shared/rpmb/rpmb_listener.c
index 7cf0b79..c9623e2 100644
--- a/platform/msm_shared/rpmb/rpmb_listener.c
+++ b/platform/msm_shared/rpmb/rpmb_listener.c
@@ -67,6 +67,8 @@
uint32_t num_sectors;
uint32_t req_buff_len;
uint32_t req_buff_offset;
+ uint32_t version;
+ uint32_t rel_wr_count;
}__PACKED;
struct tz_rpmb_rw_resp
@@ -75,6 +77,7 @@
int32_t status;
uint32_t res_buff_len;
uint32_t res_buff_offset;
+ uint32_t version;
}__PACKED;
typedef int (*ListenerCallback)(void*, uint32_t);
@@ -113,14 +116,16 @@
case TZ_CM_CMD_RPMB_READ:
#if DEBUG_RPMB
dprintf(INFO, "Read Request received\n");
+ dprintf(INFO, "READ: RPMB_REL_RW_COUNT 0x%x\n", req_p->rel_wr_count);
#endif
resp_p->status = rpmb_read(req_buf, req_p->num_sectors, resp_buf, &resp_p->res_buff_len);
break;
case TZ_CM_CMD_RPMB_WRITE:
#if DEBUG_RPMB
dprintf(INFO, "Write Request received\n");
+ dprintf(INFO, "WRITE: RPMB_REL_RW_COUNT 0x%x\n", req_p->rel_wr_count);
#endif
- resp_p->status = rpmb_write(req_buf, req_p->num_sectors, resp_buf, &resp_p->res_buff_len);
+ resp_p->status = rpmb_write(req_buf, req_p->num_sectors, req_p->rel_wr_count, resp_buf, &resp_p->res_buff_len);
break;
default:
dprintf(CRITICAL, "Unsupported request command request: %u\n", req_p->cmd_id);
@@ -174,7 +179,7 @@
rpmb_listener.service_name = "RPMB system services";
rpmb_listener.id = RPMB_LSTNR_ID;
- rpmb_listener.sb_size = 20 * 1024;
+ rpmb_listener.sb_size = 25 * 1024;
rpmb_listener.service_cmd_handler = rpmb_cmd_handler;
ret = qseecom_register_listener(&rpmb_listener);
diff --git a/platform/msm_shared/rpmb/rpmb_ufs.c b/platform/msm_shared/rpmb/rpmb_ufs.c
index 3cc4d91..17cfc56 100644
--- a/platform/msm_shared/rpmb/rpmb_ufs.c
+++ b/platform/msm_shared/rpmb/rpmb_ufs.c
@@ -36,6 +36,18 @@
#include <endian.h>
#include <arch/defines.h>
+static const char *str_err[] =
+{
+ "Operation Ok",
+ "General failure",
+ "Authentication error (MAC comparison not matching, MAC calculation failure)",
+ "Counter failure (counters not matching in comparison, counter incrementing failure)",
+ "Address failure (address out of range, wrong address alignment)",
+ "Write failure (data/counter/result write failure)",
+ "Read failure (data/counter/result read failure)",
+ "Authentication Key not yet programmed",
+};
+
static struct rpmb_frame read_result_reg =
{
.requestresponse[1] = READ_RESULT_FLAG,
@@ -71,8 +83,8 @@
return -UFS_FAILURE;
}
#ifdef DEBUG_RPMB
- dprintf(SPEW, "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",
+ 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%llx blks_to_transfer: 0x%x\n",
bytes_to_transfer, blks_to_transfer);
#endif
// send the request
@@ -138,15 +150,18 @@
dprintf(CRITICAL, "%s:%d ucs_do_scsi_rpmb_read: failed\n", __func__, __LINE__);
return -UFS_FAILURE;
}
+ // invalidate response buffer before reading response as this is part of request
+ // buffer overwritten
+ arch_clean_invalidate_cache_range((addr_t) resp_buf, RPMB_FRAME_SIZE);
#ifdef DEBUG_RPMB
- dprintf(SPEW, "Sending RPMB Read response complete\n");
+ dprintf(INFO, "Sending RPMB Read response complete\n");
dump_rpmb_frame((uint8_t *)resp_buf, "response");
#endif
*resp_len = bytes_to_transfer;
return UFS_SUCCESS;
}
-int rpmb_write_ufs(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t *resp_buf, uint32_t *resp_len)
+int rpmb_write_ufs(struct ufs_dev *dev, uint32_t *req_buf, uint32_t blk_cnt, uint32_t rel_wr_count, uint32_t *resp_buf, uint32_t *resp_len)
{
// validate input parameters
ASSERT(req_buf);
@@ -156,12 +171,14 @@
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;
+ struct rpmb_frame *result = (struct rpmb_frame *)resp_buf;
uint32_t blks_remaining;
uint32_t blks_to_transfer;
uint64_t bytes_to_transfer;
uint64_t max_size;
uint32_t result_frame_bytes = RPMB_FRAME_SIZE;
uint32_t i;
+ uint32_t num_trans;
blks_remaining = blk_cnt;
blks_to_transfer = blks_remaining;
@@ -176,13 +193,31 @@
}
#ifdef DEBUG_RPMB
dprintf(INFO, "%s: req_buf: 0x%x blk_count: 0x%x\n", __func__,*req_buf, blk_cnt);
- dprintf(INFO, "%s: bytes_to_transfer: 0x%x blks_to_transfer: 0x%x\n", __func__
- bytes_to_transfer, blk_cnt);
- dump_rpmb_frame((uint8_t *)req_buf, "request");
+ dprintf(INFO, "%s: bytes_to_transfer: 0x%llx blks_to_transfer: 0x%x\n", __func__, bytes_to_transfer, blk_cnt);
#endif
-
- for (i = 0; i < blk_cnt; i++)
+ if (bytes_to_transfer <= (rel_wr_count * RPMB_FRAME_SIZE))
{
+ num_trans = 1;
+ }
+ else
+ {
+ // send uptop rel_wr_count number of frames in one shot + anything pending
+ num_trans = blk_cnt/rel_wr_count;
+ if (num_trans % rel_wr_count) // if anymore frames still pending
+ num_trans++;
+ }
+ for (i = 0; i < num_trans; i++)
+ {
+ if ((num_trans - i) == 1) // last loop or one and only loop
+ {
+ // last loop will contain a max of rel_wr_count number of frames or less
+ bytes_to_transfer = RPMB_FRAME_SIZE * blks_remaining;
+ }
+ else
+ {
+ bytes_to_transfer = RPMB_FRAME_SIZE * rel_wr_count;
+ }
+
// send the request
cdb_out_param = (struct scsi_sec_protocol_cdb*) cdb;
memset(cdb_out_param, 0, sizeof(struct scsi_sec_protocol_cdb));
@@ -190,6 +225,44 @@
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 = (addr_t) 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_RPMB
+ dprintf(INFO, "Sending RPMB write request: Start\n");
+ for(uint8_t j = 0; j < (bytes_to_transfer/RPMB_FRAME_SIZE); j++)
+ {
+ dprintf(INFO, "request buffer address: %p\n", (req_buf + (j * RPMB_FRAME_SIZE)));
+ dump_rpmb_frame((uint8_t *)req_buf + (j * RPMB_FRAME_SIZE), "request");
+ }
+#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_RPMB
+ dprintf(INFO, "Sending RPMB write request: Done\n");
+#endif
+ // Result Read
+ 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(RPMB_FRAME_SIZE);
// Flush CDB to memory
@@ -199,39 +272,6 @@
memset(&req_upiu, 0, sizeof(struct scsi_req_build_type));
req_upiu.cdb = (addr_t) cdb_out_param;
- req_upiu.data_buffer_addr = (addr_t) req_buf;
- req_upiu.data_len = RPMB_FRAME_SIZE;
- req_upiu.flags = UPIU_FLAGS_WRITE;
- req_upiu.lun = UFS_WLUN_RPMB;
- req_upiu.dd = UTRD_TARGET_TO_SYSTEM;
-
-#ifdef DEBUG_RPMB
- dprintf(INFO, "Sending RPMB write request: Start\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_RPMB
- dprintf(INFO, "Sending RPMB write request: Done\n");
-#endif
- // Result Read
- 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_OUT;
- 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(RPMB_FRAME_SIZE);
-
- // 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 = (addr_t) &read_result_reg ;
req_upiu.data_len = result_frame_bytes;
req_upiu.flags = UPIU_FLAGS_WRITE;
@@ -247,7 +287,7 @@
return -UFS_FAILURE;
}
#ifdef DEBUG_RPMB
- dprintf(SPEW, "Sending RPMB Result Read Register: Done\n");
+ dprintf(INFO, "Sending RPMB Result Read Register: Done\n");
#endif
// Retrieve verification result
@@ -280,12 +320,34 @@
dprintf(CRITICAL, "%s:%d ucs_do_scsi_rpmb_read: failed\n", __func__, __LINE__);
return -UFS_FAILURE;
}
+ // invalidate response buffer before reading response as this is part of request
+ // buffer overwritten
+ arch_clean_invalidate_cache_range((addr_t) resp_buf, result_frame_bytes);
#ifdef DEBUG_RPMB
- dprintf(SPEW, "Sending RPMB Response for Result Read Register: Done\n");
+ dprintf(INFO, "Sending RPMB Response for Result Read Register: Done\n");
dump_rpmb_frame((uint8_t *)resp_buf, "response");
#endif
- req_buf = (uint32_t*) ((uint8_t*)req_buf + RPMB_BLK_SIZE);
+ if (result->result[0] == 0x80)
+ {
+ dprintf(CRITICAL, "Max write counter reached: \n");
+ break;
+ }
+
+ if (result->result[1])
+ {
+ dprintf(CRITICAL, "UFS RPMB write error: %s\n", str_err[result->result[1]]);
+ break;
+ }
+ if ((num_trans - i) == 1)
+ continue; // last frame no need to increment
+ else
+ {
+ req_buf = (uint32_t*) ((uint8_t*)req_buf + (RPMB_BLK_SIZE * rel_wr_count));
+ // If more than 1 transaction, then until the last loop, we will be transfering
+ // rel_wr_count number of half sections in one transaction
+ blks_remaining = blks_remaining - rel_wr_count;
+ }
}
*resp_len = RPMB_MIN_BLK_CNT * RPMB_BLK_SIZE;
diff --git a/platform/msm_shared/sdhci.c b/platform/msm_shared/sdhci.c
index 72126bf..bfab9f1 100644
--- a/platform/msm_shared/sdhci.c
+++ b/platform/msm_shared/sdhci.c
@@ -863,7 +863,7 @@
/* Enable auto cmd23 or cmd12 for multi block transfer
* based on what command card supports
*/
- if (cmd->data.num_blocks > 1) {
+ if ((cmd->data.num_blocks > 1) && !cmd->rel_write) {
if (cmd->cmd23_support) {
trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD23_EN | SDHCI_BLK_CNT_EN;
REG_WRITE32(host, cmd->data.num_blocks, SDHCI_ARG2_REG);
@@ -871,6 +871,9 @@
else
trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD12_EN | SDHCI_BLK_CNT_EN;
}
+ else if ((cmd->data.num_blocks > 1) && cmd->rel_write) {
+ trans_mode |= SDHCI_TRANS_MULTI | SDHCI_BLK_CNT_EN;
+ }
}
/* Write to transfer mode register */