zynqmp: pm: Implement IOCTL APIs for device control

Implement ioctl APIs which uses MMIO operations
to configure devices. Below IOCTLs are supported
in this patch:
  * Set tap delay bypass
  * Set SGMII mode
  * SD reset
  * Set SD/MMC tap delay

Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c
index 6f94f42..c881d12 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c
@@ -141,6 +141,195 @@
 }
 
 /**
+ * pm_ioctl_set_tapdelay_bypass() -  Enable/Disable tap delay bypass
+ * @type	Type of tap delay to enable/disable (e.g. QSPI)
+ * @value	Enable/Disable
+ *
+ * This function enable/disable tap delay bypass.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_set_tapdelay_bypass(unsigned int type,
+						       unsigned int value)
+{
+	if ((value != PM_TAPDELAY_BYPASS_ENABLE &&
+	     value != PM_TAPDELAY_BYPASS_DISABLE) || type >= PM_TAPDELAY_MAX)
+		return PM_RET_ERROR_ARGS;
+
+	return pm_mmio_write(IOU_TAPDLY_BYPASS, TAP_DELAY_MASK, value << type);
+}
+
+/**
+ * pm_ioctl_set_sgmii_mode() -  Set SGMII mode for the GEM device
+ * @nid		Node ID of the device
+ * @value	Enable/Disable
+ *
+ * This function enable/disable SGMII mode for the GEM device.
+ * While enabling SGMII mode, it also ties the GEM PCS Signal
+ * Detect to 1 and selects EMIO for RX clock generation.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_set_sgmii_mode(enum pm_node_id nid,
+						  unsigned int value)
+{
+	unsigned int val, mask, shift;
+	int ret;
+
+	if (value != PM_SGMII_DISABLE && value != PM_SGMII_ENABLE)
+		return PM_RET_ERROR_ARGS;
+
+	switch (nid) {
+	case NODE_ETH_0:
+		shift = 0;
+		break;
+	case NODE_ETH_1:
+		shift = 1;
+		break;
+	case NODE_ETH_2:
+		shift = 2;
+		break;
+	case NODE_ETH_3:
+		shift = 3;
+		break;
+	default:
+		return PM_RET_ERROR_ARGS;
+	}
+
+	if (value == PM_SGMII_DISABLE) {
+		mask = GEM_SGMII_MASK << GEM_CLK_CTRL_OFFSET * shift;
+		ret = pm_mmio_write(IOU_GEM_CLK_CTRL, mask, 0);
+	} else {
+		/* Tie the GEM PCS Signal Detect to 1 */
+		mask = SGMII_SD_MASK << SGMII_SD_OFFSET * shift;
+		val = SGMII_PCS_SD_1 << SGMII_SD_OFFSET * shift;
+		ret = pm_mmio_write(IOU_GEM_CTRL, mask, val);
+		if (ret)
+			return ret;
+
+		/* Set the GEM to SGMII mode */
+		mask = GEM_CLK_CTRL_MASK << GEM_CLK_CTRL_OFFSET * shift;
+		val = GEM_RX_SRC_SEL_GTR | GEM_SGMII_MODE;
+		val <<= GEM_CLK_CTRL_OFFSET * shift;
+		ret =  pm_mmio_write(IOU_GEM_CLK_CTRL, mask, val);
+	}
+
+	return ret;
+}
+
+/**
+ * pm_ioctl_sd_dll_reset() -  Reset DLL logic
+ * @nid		Node ID of the device
+ * @type	Reset type
+ *
+ * This function resets DLL logic for the SD device.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_sd_dll_reset(enum pm_node_id nid,
+						unsigned int type)
+{
+	unsigned int mask, val;
+	int ret;
+
+	if (nid == NODE_SD_0) {
+		mask = ZYNQMP_SD0_DLL_RST_MASK;
+		val = ZYNQMP_SD0_DLL_RST;
+	} else if (nid == NODE_SD_1) {
+		mask = ZYNQMP_SD1_DLL_RST_MASK;
+		val = ZYNQMP_SD1_DLL_RST;
+	} else {
+		return PM_RET_ERROR_ARGS;
+	}
+
+	switch (type) {
+	case PM_DLL_RESET_ASSERT:
+	case PM_DLL_RESET_PULSE:
+		ret = pm_mmio_write(ZYNQMP_SD_DLL_CTRL, mask, val);
+		if (ret)
+			return ret;
+
+		if (type == PM_DLL_RESET_ASSERT)
+			break;
+		mdelay(1);
+	case PM_DLL_RESET_RELEASE:
+		ret = pm_mmio_write(ZYNQMP_SD_DLL_CTRL, mask, 0);
+		break;
+	default:
+		ret = PM_RET_ERROR_ARGS;
+	}
+
+	return ret;
+}
+
+/**
+ * pm_ioctl_sd_set_tapdelay() -  Set tap delay for the SD device
+ * @nid		Node ID of the device
+ * @type	Type of tap delay to set (input/output)
+ * @value	Value to set fot the tap delay
+ *
+ * This function sets input/output tap delay for the SD device.
+ *
+ * @return	Returns status, either success or error+reason
+ */
+static enum pm_ret_status pm_ioctl_sd_set_tapdelay(enum pm_node_id nid,
+						   enum tap_delay_type type,
+						   unsigned int value)
+{
+	unsigned int shift;
+	int ret;
+
+	if (nid == NODE_SD_0)
+		shift = 0;
+	else if (nid == NODE_SD_1)
+		shift = ZYNQMP_SD_TAP_OFFSET;
+	else
+		return PM_RET_ERROR_ARGS;
+
+	ret = pm_ioctl_sd_dll_reset(nid, PM_DLL_RESET_ASSERT);
+	if (ret)
+		return ret;
+
+	if (type == PM_TAPDELAY_INPUT) {
+		ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY,
+				    ZYNQMP_SD_ITAPCHGWIN_MASK << shift,
+				    ZYNQMP_SD_ITAPCHGWIN << shift);
+		if (ret)
+			goto reset_release;
+		ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY,
+				    ZYNQMP_SD_ITAPDLYENA_MASK << shift,
+				    ZYNQMP_SD_ITAPDLYENA << shift);
+		if (ret)
+			goto reset_release;
+		ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY,
+				    ZYNQMP_SD_ITAPDLYSEL_MASK << shift,
+				    value << shift);
+		if (ret)
+			goto reset_release;
+		ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY,
+				    ZYNQMP_SD_ITAPCHGWIN_MASK << shift, 0);
+	} else if (type == PM_TAPDELAY_OUTPUT) {
+		ret = pm_mmio_write(ZYNQMP_SD_OTAP_DLY,
+				    ZYNQMP_SD_OTAPDLYENA_MASK << shift,
+				    ZYNQMP_SD_OTAPDLYENA << shift);
+		if (ret)
+			goto reset_release;
+		ret = pm_mmio_write(ZYNQMP_SD_OTAP_DLY,
+				    ZYNQMP_SD_OTAPDLYSEL_MASK << shift,
+				    value << shift);
+	} else {
+		ret = PM_RET_ERROR_ARGS;
+	}
+
+reset_release:
+	ret = pm_ioctl_sd_dll_reset(nid, PM_DLL_RESET_RELEASE);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+/**
  * 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
@@ -173,6 +362,18 @@
 	case IOCTL_TCM_COMB_CONFIG:
 		ret = pm_ioctl_config_tcm_comb(arg1);
 		break;
+	case IOCTL_SET_TAPDELAY_BYPASS:
+		ret = pm_ioctl_set_tapdelay_bypass(arg1, arg2);
+		break;
+	case IOCTL_SET_SGMII_MODE:
+		ret = pm_ioctl_set_sgmii_mode(nid, arg1);
+		break;
+	case IOCTL_SD_DLL_RESET:
+		ret = pm_ioctl_sd_dll_reset(nid, arg1);
+		break;
+	case IOCTL_SET_SD_TAPDELAY:
+		ret = pm_ioctl_sd_set_tapdelay(nid, arg1, arg2);
+		break;
 	default:
 		ret = PM_RET_ERROR_NOTSUPPORTED;
 	}
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h
index 9dae69f..a7f14a4 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h
@@ -18,6 +18,10 @@
 	IOCTL_SET_RPU_OPER_MODE,
 	IOCTL_RPU_BOOT_ADDR_CONFIG,
 	IOCTL_TCM_COMB_CONFIG,
+	IOCTL_SET_TAPDELAY_BYPASS,
+	IOCTL_SET_SGMII_MODE,
+	IOCTL_SD_DLL_RESET,
+	IOCTL_SET_SD_TAPDELAY,
 };
 
 enum rpu_oper_mode {
@@ -35,6 +39,34 @@
 	PM_RPU_TCM_COMB,
 };
 
+enum tap_delay_signal_type {
+	PM_TAPDELAY_NAND_DQS_IN,
+	PM_TAPDELAY_NAND_DQS_OUT,
+	PM_TAPDELAY_QSPI,
+	PM_TAPDELAY_MAX,
+};
+
+enum tap_delay_bypass_ctrl {
+	PM_TAPDELAY_BYPASS_DISABLE,
+	PM_TAPDELAY_BYPASS_ENABLE,
+};
+
+enum sgmii_mode {
+	PM_SGMII_DISABLE,
+	PM_SGMII_ENABLE,
+};
+
+enum tap_delay_type {
+	PM_TAPDELAY_INPUT,
+	PM_TAPDELAY_OUTPUT,
+};
+
+enum dll_reset_type {
+	PM_DLL_RESET_ASSERT,
+	PM_DLL_RESET_RELEASE,
+	PM_DLL_RESET_PULSE,
+};
+
 enum pm_ret_status pm_api_ioctl(enum pm_node_id nid,
 				unsigned int ioctl_id,
 				unsigned int arg1,
diff --git a/plat/xilinx/zynqmp/zynqmp_def.h b/plat/xilinx/zynqmp/zynqmp_def.h
index 54036f9..f3180e9 100644
--- a/plat/xilinx/zynqmp/zynqmp_def.h
+++ b/plat/xilinx/zynqmp/zynqmp_def.h
@@ -186,4 +186,43 @@
 #define ZYNQMP_SLCLAMP_MASK			0x10
 #define ZYNQMP_VINITHI_MASK			0x04
 
+/* Tap delay bypass */
+#define IOU_TAPDLY_BYPASS			0XFF180390
+#define TAP_DELAY_MASK				0x7
+
+/* SGMII mode */
+#define IOU_GEM_CTRL				0xFF180360
+#define IOU_GEM_CLK_CTRL			0xFF180308
+#define SGMII_SD_MASK				0x3
+#define SGMII_SD_OFFSET				2
+#define SGMII_PCS_SD_0				0x0
+#define SGMII_PCS_SD_1				0x1
+#define SGMII_PCS_SD_PHY			0x2
+#define GEM_SGMII_MASK				0x4
+#define GEM_CLK_CTRL_MASK			0xF
+#define GEM_CLK_CTRL_OFFSET			5
+#define GEM_RX_SRC_SEL_GTR			0x1
+#define GEM_SGMII_MODE				0x4
+
+/* SD DLL reset */
+#define ZYNQMP_SD_DLL_CTRL			0xFF180358
+#define ZYNQMP_SD0_DLL_RST_MASK			0x00000004
+#define ZYNQMP_SD0_DLL_RST			0x00000004
+#define ZYNQMP_SD1_DLL_RST_MASK			0x00040000
+#define ZYNQMP_SD1_DLL_RST			0x00040000
+
+/* SD tap delay */
+#define ZYNQMP_SD_DLL_CTRL			0xFF180358
+#define ZYNQMP_SD_ITAP_DLY			0xFF180314
+#define ZYNQMP_SD_OTAP_DLY			0xFF180318
+#define ZYNQMP_SD_TAP_OFFSET			16
+#define ZYNQMP_SD_ITAPCHGWIN_MASK		0x200
+#define ZYNQMP_SD_ITAPCHGWIN			0x200
+#define ZYNQMP_SD_ITAPDLYENA_MASK		0x100
+#define ZYNQMP_SD_ITAPDLYENA			0x100
+#define ZYNQMP_SD_ITAPDLYSEL_MASK		0xFF
+#define ZYNQMP_SD_OTAPDLYSEL_MASK		0x3F
+#define ZYNQMP_SD_OTAPDLYENA_MASK		0x40
+#define ZYNQMP_SD_OTAPDLYENA			0x40
+
 #endif /* __ZYNQMP_DEF_H__ */