zynqmp: pm: Implement IOCTL APIs for remoteproc

Implement ioctl APIs which uses MMIO operations
to control RPU operations. Below IOCTLs are supported
in this patch:
  * Get RPU operation mode
  * Set RPU operation mode
  * Configure RPU boot address (OCM/TCM)
  * Configure TCM combined mode

Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk
index cefb73a..2abcd28 100644
--- a/plat/xilinx/zynqmp/platform.mk
+++ b/plat/xilinx/zynqmp/platform.mk
@@ -78,6 +78,7 @@
 				plat/xilinx/zynqmp/pm_service/pm_svc_main.c	\
 				plat/xilinx/zynqmp/pm_service/pm_api_sys.c	\
 				plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c	\
+				plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c	\
 				plat/xilinx/zynqmp/pm_service/pm_ipi.c		\
 				plat/xilinx/zynqmp/pm_service/pm_client.c	\
 				plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.c
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c
new file mode 100644
index 0000000..6f94f42
--- /dev/null
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * ZynqMP system level PM-API functions for ioctl.
+ */
+
+#include <arch_helpers.h>
+#include <delay_timer.h>
+#include <mmio.h>
+#include <platform.h>
+#include "pm_api_ioctl.h"
+#include "pm_api_sys.h"
+#include "pm_client.h"
+#include "pm_common.h"
+#include "pm_ipi.h"
+#include "../zynqmp_def.h"
+
+/**
+ * pm_ioctl_get_rpu_oper_mode () - Get current RPU operation mode
+ * @mode	Buffer to store value of oper mode(Split/Lock-step)
+ *
+ * This function provides current configured RPU operational mode.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_get_rpu_oper_mode(unsigned int *mode)
+{
+	unsigned int val;
+
+	val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL);
+	val &= ZYNQMP_SLSPLIT_MASK;
+	if (val)
+		*mode = PM_RPU_MODE_SPLIT;
+	else
+		*mode = PM_RPU_MODE_LOCKSTEP;
+
+	return PM_RET_SUCCESS;
+}
+
+/**
+ * pm_ioctl_set_rpu_oper_mode () - Configure RPU operation mode
+ * @mode	Value to set for oper mode(Split/Lock-step)
+ *
+ * This function configures RPU operational mode(Split/Lock-step).
+ * It also sets TCM combined mode in RPU lock-step and TCM non-combined
+ * mode for RPU split mode. In case of Lock step mode, RPU1's output is
+ * clamped.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_set_rpu_oper_mode(unsigned int mode)
+{
+	unsigned int val;
+
+	if (mmio_read_32(CRL_APB_RST_LPD_TOP) && CRL_APB_RPU_AMBA_RESET)
+		return PM_RET_ERROR_ACCESS;
+
+	val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL);
+
+	if (mode == PM_RPU_MODE_SPLIT) {
+		val |= ZYNQMP_SLSPLIT_MASK;
+		val &= ~ZYNQMP_TCM_COMB_MASK;
+		val &= ~ZYNQMP_SLCLAMP_MASK;
+	} else if (mode == PM_RPU_MODE_LOCKSTEP) {
+		val &= ~ZYNQMP_SLSPLIT_MASK;
+		val |= ZYNQMP_TCM_COMB_MASK;
+		val |= ZYNQMP_SLCLAMP_MASK;
+	} else {
+		return PM_RET_ERROR_ARGS;
+	}
+
+	mmio_write_32(ZYNQMP_RPU_GLBL_CNTL, val);
+
+	return PM_RET_SUCCESS;
+}
+
+/**
+ * pm_ioctl_config_boot_addr() - Configure RPU boot address
+ * @nid		Node ID of RPU
+ * @value	Value to set for boot address (TCM/OCM)
+ *
+ * This function configures RPU boot address(memory).
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_config_boot_addr(enum pm_node_id nid,
+						    unsigned int value)
+{
+	unsigned int rpu_cfg_addr, val;
+
+	if (nid == NODE_RPU_0)
+		rpu_cfg_addr = ZYNQMP_RPU0_CFG;
+	else if (nid == NODE_RPU_1)
+		rpu_cfg_addr = ZYNQMP_RPU1_CFG;
+	else
+		return PM_RET_ERROR_ARGS;
+
+	val = mmio_read_32(rpu_cfg_addr);
+
+	if (value == PM_RPU_BOOTMEM_LOVEC)
+		val &= ~ZYNQMP_VINITHI_MASK;
+	else if (value == PM_RPU_BOOTMEM_HIVEC)
+		val |= ZYNQMP_VINITHI_MASK;
+	else
+		return PM_RET_ERROR_ARGS;
+
+	mmio_write_32(rpu_cfg_addr, val);
+
+	return PM_RET_SUCCESS;
+}
+
+/**
+ * pm_ioctl_config_tcm_comb() - Configure TCM combined mode
+ * @value	Value to set (Split/Combined)
+ *
+ * This function configures TCM to be in split mode or combined
+ * mode.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_config_tcm_comb(unsigned int value)
+{
+	unsigned int val;
+
+	val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL);
+
+	if (value == PM_RPU_TCM_SPLIT)
+		val &= ~ZYNQMP_TCM_COMB_MASK;
+	else if (value == PM_RPU_TCM_COMB)
+		val |= ZYNQMP_TCM_COMB_MASK;
+	else
+		return PM_RET_ERROR_ARGS;
+
+	mmio_write_32(ZYNQMP_RPU_GLBL_CNTL, val);
+
+	return PM_RET_SUCCESS;
+}
+
+/**
+ * pm_api_ioctl() -  PM IOCTL API for device control and configs
+ * @node_id	Node ID of the device
+ * @ioctl_id	ID of the requested IOCTL
+ * @arg1	Argument 1 to requested IOCTL call
+ * @arg2	Argument 2 to requested IOCTL call
+ * @value	Returned output value
+ *
+ * This function calls IOCTL to firmware for device control and configuration.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+enum pm_ret_status pm_api_ioctl(enum pm_node_id nid,
+				unsigned int ioctl_id,
+				unsigned int arg1,
+				unsigned int arg2,
+				unsigned int *value)
+{
+	int ret;
+
+	switch (ioctl_id) {
+	case IOCTL_GET_RPU_OPER_MODE:
+		ret = pm_ioctl_get_rpu_oper_mode(value);
+		break;
+	case IOCTL_SET_RPU_OPER_MODE:
+		ret = pm_ioctl_set_rpu_oper_mode(arg1);
+		break;
+	case IOCTL_RPU_BOOT_ADDR_CONFIG:
+		ret = pm_ioctl_config_boot_addr(nid, arg1);
+		break;
+	case IOCTL_TCM_COMB_CONFIG:
+		ret = pm_ioctl_config_tcm_comb(arg1);
+		break;
+	default:
+		ret = PM_RET_ERROR_NOTSUPPORTED;
+	}
+
+	return ret;
+}
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h
new file mode 100644
index 0000000..9dae69f
--- /dev/null
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * ZynqMP system level PM-API functions for pin control.
+ */
+
+#ifndef _PM_API_IOCTL_H_
+#define _PM_API_IOCTL_H_
+
+#include "pm_common.h"
+
+enum pm_ioctl_id {
+	IOCTL_GET_RPU_OPER_MODE,
+	IOCTL_SET_RPU_OPER_MODE,
+	IOCTL_RPU_BOOT_ADDR_CONFIG,
+	IOCTL_TCM_COMB_CONFIG,
+};
+
+enum rpu_oper_mode {
+	PM_RPU_MODE_LOCKSTEP,
+	PM_RPU_MODE_SPLIT,
+};
+
+enum rpu_boot_mem {
+	PM_RPU_BOOTMEM_LOVEC,
+	PM_RPU_BOOTMEM_HIVEC,
+};
+
+enum rpu_tcm_comb {
+	PM_RPU_TCM_SPLIT,
+	PM_RPU_TCM_COMB,
+};
+
+enum pm_ret_status pm_api_ioctl(enum pm_node_id nid,
+				unsigned int ioctl_id,
+				unsigned int arg1,
+				unsigned int arg2,
+				unsigned int *value);
+#endif /* _PM_API_IOCTL_H_ */
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
index 14c78fd..be24371 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
@@ -11,6 +11,7 @@
 
 #include <arch_helpers.h>
 #include <platform.h>
+#include "pm_api_ioctl.h"
 #include "pm_api_pinctrl.h"
 #include "pm_api_sys.h"
 #include "pm_client.h"
@@ -636,3 +637,24 @@
 {
 	return pm_api_pinctrl_set_config(pin, param, value);
 }
+
+/**
+ * pm_ioctl() -  PM IOCTL API for device control and configs
+ * @node_id	Node ID of the device
+ * @ioctl_id	ID of the requested IOCTL
+ * @arg1	Argument 1 to requested IOCTL call
+ * @arg2	Argument 2 to requested IOCTL call
+ * @out		Returned output value
+ *
+ * This function calls IOCTL to firmware for device control and configuration.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+enum pm_ret_status pm_ioctl(enum pm_node_id nid,
+			    unsigned int ioctl_id,
+			    unsigned int arg1,
+			    unsigned int arg2,
+			    unsigned int *value)
+{
+	return pm_api_ioctl(nid, ioctl_id, arg1, arg2, value);
+}
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
index f240d49..1c91e8f 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
@@ -105,5 +105,10 @@
 enum pm_ret_status pm_pinctrl_set_config(unsigned int pin,
 					 unsigned int param,
 					 unsigned int value);
+enum pm_ret_status pm_ioctl(enum pm_node_id nid,
+			    unsigned int ioctl_id,
+			    unsigned int arg1,
+			    unsigned int arg2,
+			    unsigned int *value);
 
 #endif /* _PM_API_SYS_H_ */
diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h
index c9656e3..afd92f6 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_defs.h
+++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h
@@ -75,6 +75,7 @@
 	PM_PINCTRL_SET_FUNCTION,
 	PM_PINCTRL_CONFIG_PARAM_GET,
 	PM_PINCTRL_CONFIG_PARAM_SET,
+	PM_IOCTL,
 	PM_API_MAX
 };
 
diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
index 22b6bc3..648de24 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
@@ -280,6 +280,15 @@
 		ret = pm_pinctrl_set_config(pm_arg[0], pm_arg[1], pm_arg[2]);
 		SMC_RET1(handle, (uint64_t)ret);
 
+	case PM_IOCTL:
+	{
+		uint32_t value;
+
+		ret = pm_ioctl(pm_arg[0], pm_arg[1], pm_arg[2],
+			       pm_arg[3], &value);
+		SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32);
+	}
+
 	default:
 		WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid);
 		SMC_RET1(handle, SMC_UNK);
diff --git a/plat/xilinx/zynqmp/zynqmp_def.h b/plat/xilinx/zynqmp/zynqmp_def.h
index 45fa387..54036f9 100644
--- a/plat/xilinx/zynqmp/zynqmp_def.h
+++ b/plat/xilinx/zynqmp/zynqmp_def.h
@@ -47,7 +47,9 @@
 #define CRL_APB_RPLL_CTRL		(CRL_APB_BASE + 0x30)
 #define CRL_APB_BOOT_MODE_USER		(CRL_APB_BASE + 0x200)
 #define CRL_APB_RESET_CTRL		(CRL_APB_BASE + 0x218)
+#define CRL_APB_RST_LPD_TOP		(CRL_APB_BASE + 0x23C)
 
+#define CRL_APB_RPU_AMBA_RESET		(1 << 2)
 #define CRL_APB_RPLL_CTRL_BYPASS	(1 << 3)
 
 #define CRL_APB_RESET_CTRL_SOFT_RESET	(1 << 4)
@@ -176,4 +178,12 @@
 
 #define IOU_SLCR_BASEADDR		0xFF180000
 
+#define ZYNQMP_RPU_GLBL_CNTL			0xFF9A0000
+#define ZYNQMP_RPU0_CFG				0xFF9A0100
+#define ZYNQMP_RPU1_CFG				0xFF9A0200
+#define ZYNQMP_SLSPLIT_MASK			0x08
+#define ZYNQMP_TCM_COMB_MASK			0x40
+#define ZYNQMP_SLCLAMP_MASK			0x10
+#define ZYNQMP_VINITHI_MASK			0x04
+
 #endif /* __ZYNQMP_DEF_H__ */