intel: mailbox: Enable sending large mailbox command

Allow mailbox command that is larger than mailbox command FIFO buffer
size to be sent to SDM in multiple chunks.

Signed-off-by: Abdul Halim, Muhammad Hadi Asyrafi <muhammad.hadi.asyrafi.abdul.halim@intel.com>
Change-Id: I683d5f1d04c4fdf57d11ecae6232b7ed3fc49e26
diff --git a/plat/intel/soc/common/soc/socfpga_mailbox.c b/plat/intel/soc/common/soc/socfpga_mailbox.c
index ddfe34c..870ef10 100644
--- a/plat/intel/soc/common/soc/socfpga_mailbox.c
+++ b/plat/intel/soc/common/soc/socfpga_mailbox.c
@@ -11,32 +11,105 @@
 #include "socfpga_mailbox.h"
 #include "socfpga_sip_svc.h"
 
+
+static bool is_mailbox_cmdbuf_full(uint32_t cin)
+{
+	uint32_t cout = mmio_read_32(MBOX_OFFSET + MBOX_COUT);
+
+	return (((cin + 1U) % MBOX_CMD_BUFFER_SIZE) == cout);
+}
+
+static bool is_mailbox_cmdbuf_empty(uint32_t cin)
+{
+	uint32_t cout = mmio_read_32(MBOX_OFFSET + MBOX_COUT);
+
+	return (((cout + 1U) % MBOX_CMD_BUFFER_SIZE) == cin);
+}
+
+static int wait_for_mailbox_cmdbuf_empty(uint32_t cin)
+{
+	uint32_t timeout = 200U;
+
+	do {
+		if (is_mailbox_cmdbuf_empty(cin)) {
+			break;
+		}
+		mdelay(10U);
+	} while (--timeout != 0U);
+
+	if (timeout == 0U) {
+		return MBOX_TIMEOUT;
+	}
+
+	return MBOX_RET_OK;
+}
+
+static int write_mailbox_cmd_buffer(uint32_t *cin, uint32_t cout,
+				    uint32_t data,
+				    bool *is_doorbell_triggered)
+{
+	uint32_t timeout = 100U;
+
+	do {
+		if (is_mailbox_cmdbuf_full(*cin)) {
+			if (!(*is_doorbell_triggered)) {
+				mmio_write_32(MBOX_OFFSET +
+					      MBOX_DOORBELL_TO_SDM, 1);
+				*is_doorbell_triggered = true;
+			}
+			mdelay(10U);
+		} else {
+			mmio_write_32(MBOX_OFFSET + MBOX_CMD_BUFFER +
+				      (*cin * 4), data);
+			(*cin)++;
+			*cin %= MBOX_CMD_BUFFER_SIZE;
+			mmio_write_32(MBOX_OFFSET + MBOX_CIN, *cin);
+			break;
+		}
+	} while (--timeout != 0U);
+
+	if (timeout == 0U) {
+		return MBOX_TIMEOUT;
+	}
+
+	if (*is_doorbell_triggered) {
+		int ret = wait_for_mailbox_cmdbuf_empty(*cin);
+		return ret;
+	}
+
+	return MBOX_RET_OK;
+}
+
 static int fill_mailbox_circular_buffer(uint32_t header_cmd, uint32_t *args,
 					int len)
 {
 	uint32_t sdm_read_offset, cmd_free_offset;
-	int i;
+	uint32_t i;
+	int ret;
+	bool is_doorbell_triggered = false;
 
 	cmd_free_offset = mmio_read_32(MBOX_OFFSET + MBOX_CIN);
 	sdm_read_offset = mmio_read_32(MBOX_OFFSET + MBOX_COUT);
 
-	if ((cmd_free_offset < sdm_read_offset) &&
-		(cmd_free_offset + len > sdm_read_offset)) {
-		return MBOX_BUFFER_FULL;
+	ret = write_mailbox_cmd_buffer(&cmd_free_offset, sdm_read_offset,
+				       header_cmd, &is_doorbell_triggered);
+	if (ret != 0) {
+		return ret;
 	}
 
-	mmio_write_32(MBOX_OFFSET + MBOX_CMD_BUFFER + (cmd_free_offset++ * 4),
-			header_cmd);
-
-
-	for (i = 0; i < len; i++) {
-		cmd_free_offset %= MBOX_CMD_BUFFER_SIZE;
-		mmio_write_32(MBOX_OFFSET + MBOX_CMD_BUFFER +
-				(cmd_free_offset++ * 4), args[i]);
+	for (i = 0U; i < len; i++) {
+		is_doorbell_triggered = false;
+		ret = write_mailbox_cmd_buffer(&cmd_free_offset,
+					       sdm_read_offset, args[i],
+					       &is_doorbell_triggered);
+		if (ret != 0) {
+			return ret;
+		}
 	}
 
-	cmd_free_offset %= MBOX_CMD_BUFFER_SIZE;
-	mmio_write_32(MBOX_OFFSET + MBOX_CIN, cmd_free_offset);
+	if (!is_doorbell_triggered) {
+		mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1);
+	}
 
 	return MBOX_RET_OK;
 }
@@ -103,7 +176,7 @@
 			    & 1) {
 				break;
 			}
-			mdelay(25);
+			mdelay(10U);
 		} while (--timeout != 0U);
 
 		if (timeout == 0U) {
@@ -171,7 +244,7 @@
 	int rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT);
 
 	while (mbox_resp_len > 0) {
-		timeout = 40;
+		timeout = 100U;
 		mbox_resp_len--;
 		resp_data = mmio_read_32(MBOX_OFFSET +
 					MBOX_RESP_BUFFER +
@@ -189,7 +262,7 @@
 		do {
 			rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN);
 			if (rout == rin) {
-				mdelay(25);
+				mdelay(10U);
 			} else {
 				break;
 			}
@@ -219,7 +292,6 @@
 		return status;
 	}
 
-	mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1);
 	*job_id = (*job_id + 1) % MBOX_MAX_IND_JOB_ID;
 
 	return MBOX_RET_OK;
@@ -234,6 +306,7 @@
 		urgent |= mmio_read_32(MBOX_OFFSET + MBOX_STATUS) &
 					MBOX_STATUS_UA_MASK;
 		mmio_write_32(MBOX_OFFSET + MBOX_URG, cmd);
+		mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1);
 	}
 
 	else {
@@ -247,7 +320,6 @@
 	if (status)
 		return status;
 
-	mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1);
 	status = mailbox_poll_response(job_id, urgent, response, resp_len);
 
 	return status;