qseecom: Add CE2 clk init/deinit & enable/disable APIs

Uses information regarding which CE HW is used by HLOS crypto
driver and which CE HW is used by secure app running on secure
domain (TrustZone) to initialize and disable/enable clks as
needed. The information is obtained from the qseecom device
tree.

Qseecom driver will need to enable clks depending on which
crypto HW is being used by secure domain.

- For disk encryption functionality, qseecom will need to enable
  the HLOS CE HW clks before issuing a command to TZ to access and
  set certain XPU protected registers (PIPE KEY register sets).
  These registers belong to the HW used by HLOS crypto driver module,
  qcrypto.  But qcrypto cannot access them as they are XPU protected.
  Hence qseecom needs to enable the clks so that TZ can write to the
  PIPE KEY  registers (needed for disk encryption functionality).

- On request from qseecom clients, qseecom will need to enable the
  clks for the CE HW used by secure domain crypto driver (accessed
  by secure app).  This functionality is already in place.

Change-Id: Ib3a384be98e61088d3748c594010dafef475e100
Signed-off-by: Mona Hossain <mhossain@codeaurora.org>
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 188ac32..7bd7d73 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -74,6 +74,11 @@
 	QSEECOM_GENERIC,
 };
 
+enum qseecom_ce_hw_instance {
+	CLK_QSEE = 0,
+	CLK_CE_DRV,
+};
+
 static struct class *driver_class;
 static dev_t qseecom_device_no;
 static struct cdev qseecom_cdev;
@@ -85,6 +90,7 @@
 
 static DEFINE_MUTEX(qsee_bw_mutex);
 static DEFINE_MUTEX(app_access_lock);
+static DEFINE_MUTEX(clk_access_lock);
 
 struct qseecom_registered_listener_list {
 	struct list_head                 list;
@@ -117,10 +123,12 @@
 };
 
 struct qseecom_clk {
+	enum qseecom_ce_hw_instance instance;
 	struct clk *ce_core_clk;
 	struct clk *ce_clk;
 	struct clk *ce_core_src_clk;
 	struct clk *ce_bus_clk;
+	uint32_t clk_access_cnt;
 };
 
 struct qseecom_control {
@@ -148,6 +156,7 @@
 
 	uint32_t qsee_perf_client;
 	struct qseecom_clk qsee;
+	struct qseecom_clk ce_drv;
 };
 
 struct qseecom_client_handle {
@@ -1891,12 +1900,23 @@
 	return 0;
 }
 
-static int __qseecom_enable_clk(void)
+static int __qseecom_enable_clk(enum qseecom_ce_hw_instance ce)
 {
 	int rc = 0;
 	struct qseecom_clk *qclk;
 
+	if (ce == CLK_QSEE)
 		qclk = &qseecom.qsee;
+	else
+		qclk = &qseecom.ce_drv;
+
+	mutex_lock(&clk_access_lock);
+	if (qclk->clk_access_cnt > 0) {
+		qclk->clk_access_cnt++;
+		mutex_unlock(&clk_access_lock);
+		return rc;
+	}
+
 	/* Enable CE core clk */
 	rc = clk_prepare_enable(qclk->ce_core_clk);
 	if (rc) {
@@ -1915,6 +1935,8 @@
 		pr_err("Unable to enable/prepare CE bus clk\n");
 		goto ce_bus_clk_err;
 	}
+	qclk->clk_access_cnt++;
+	mutex_unlock(&clk_access_lock);
 	return 0;
 
 ce_bus_clk_err:
@@ -1922,20 +1944,30 @@
 ce_clk_err:
 	clk_disable_unprepare(qclk->ce_core_clk);
 err:
+	mutex_unlock(&clk_access_lock);
 	return -EIO;
 }
 
-static void __qseecom_disable_clk(void)
+static void __qseecom_disable_clk(enum qseecom_ce_hw_instance ce)
 {
 	struct qseecom_clk *qclk;
 
-	qclk = &qseecom.qsee;
-	if (qclk->ce_clk != NULL)
-		clk_disable_unprepare(qclk->ce_clk);
-	if (qclk->ce_core_clk != NULL)
-		clk_disable_unprepare(qclk->ce_core_clk);
-	if (qclk->ce_bus_clk != NULL)
-		clk_disable_unprepare(qclk->ce_bus_clk);
+	if (ce == CLK_QSEE)
+		qclk = &qseecom.qsee;
+	else
+		qclk = &qseecom.ce_drv;
+
+	mutex_lock(&clk_access_lock);
+	if (qclk->clk_access_cnt == 1) {
+		if (qclk->ce_clk != NULL)
+			clk_disable_unprepare(qclk->ce_clk);
+		if (qclk->ce_core_clk != NULL)
+			clk_disable_unprepare(qclk->ce_core_clk);
+		if (qclk->ce_bus_clk != NULL)
+			clk_disable_unprepare(qclk->ce_bus_clk);
+	}
+	qclk->clk_access_cnt--;
+	mutex_unlock(&clk_access_lock);
 }
 
 static int qsee_vote_for_clock(struct qseecom_dev_handle *data,
@@ -1957,14 +1989,14 @@
 					qseecom.qsee_perf_client, 3);
 			else {
 				if (qclk->ce_core_src_clk != NULL)
-					ret = __qseecom_enable_clk();
+					ret = __qseecom_enable_clk(CLK_QSEE);
 				if (!ret) {
 					ret =
 					msm_bus_scale_client_update_request(
 						qseecom.qsee_perf_client, 1);
 					if ((ret) &&
 						(qclk->ce_core_src_clk != NULL))
-						__qseecom_disable_clk();
+						__qseecom_disable_clk(CLK_QSEE);
 				}
 			}
 			if (ret)
@@ -1988,14 +2020,14 @@
 					qseecom.qsee_perf_client, 3);
 			else {
 				if (qclk->ce_core_src_clk != NULL)
-					ret = __qseecom_enable_clk();
+					ret = __qseecom_enable_clk(CLK_QSEE);
 				if (!ret) {
 					ret =
 					msm_bus_scale_client_update_request(
 						qseecom.qsee_perf_client, 2);
 					if ((ret) &&
 						(qclk->ce_core_src_clk != NULL))
-						__qseecom_disable_clk();
+						__qseecom_disable_clk(CLK_QSEE);
 				}
 			}
 
@@ -2046,7 +2078,7 @@
 				ret = msm_bus_scale_client_update_request(
 						qseecom.qsee_perf_client, 0);
 				if ((!ret) && (qclk->ce_core_src_clk != NULL))
-					__qseecom_disable_clk();
+					__qseecom_disable_clk(CLK_QSEE);
 			}
 			if (ret)
 				pr_err("SFPB Bandwidth req fail (%d)\n",
@@ -2076,7 +2108,7 @@
 				ret = msm_bus_scale_client_update_request(
 						qseecom.qsee_perf_client, 0);
 				if ((!ret) && (qclk->ce_core_src_clk != NULL))
-					__qseecom_disable_clk();
+					__qseecom_disable_clk(CLK_QSEE);
 			}
 			if (ret)
 				pr_err("SFPB Bandwidth req fail (%d)\n",
@@ -2345,11 +2377,13 @@
 	memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
 	ireq.flags = flags;
 
+	__qseecom_enable_clk(CLK_QSEE);
 	ret = scm_call(SCM_SVC_CRYPTO, QSEOS_GENERATE_KEY,
 				&ireq, sizeof(struct qseecom_key_generate_ireq),
 				&resp, sizeof(resp));
 	if (ret) {
 		pr_err("scm call to generate key failed : %d\n", ret);
+		__qseecom_disable_clk(CLK_QSEE);
 		return ret;
 	}
 
@@ -2367,6 +2401,7 @@
 		ret = -EINVAL;
 		break;
 	}
+	__qseecom_disable_clk(CLK_QSEE);
 	return ret;
 }
 
@@ -2386,11 +2421,13 @@
 	memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
 	ireq.flags = flags;
 
+	__qseecom_enable_clk(CLK_QSEE);
 	ret = scm_call(SCM_SVC_CRYPTO, QSEOS_DELETE_KEY,
 				&ireq, sizeof(struct qseecom_key_delete_ireq),
 				&resp, sizeof(struct qseecom_command_scm_resp));
 	if (ret) {
 		pr_err("scm call to delete key failed : %d\n", ret);
+		__qseecom_disable_clk(CLK_QSEE);
 		return ret;
 	}
 
@@ -2409,6 +2446,7 @@
 		ret = -EINVAL;
 		break;
 	}
+	__qseecom_disable_clk(CLK_QSEE);
 	return ret;
 }
 
@@ -2424,6 +2462,12 @@
 			pr_err("Error:: unsupported usage %d\n", usage);
 			return -EFAULT;
 	}
+
+	if (qseecom.qsee.instance == qseecom.ce_drv.instance)
+		__qseecom_enable_clk(CLK_QSEE);
+	else
+		__qseecom_enable_clk(CLK_CE_DRV);
+
 	memcpy(ireq.key_id, set_key_para->key_id, QSEECOM_KEY_ID_SIZE);
 	ireq.ce = set_key_para->ce_hw;
 	ireq.pipe = set_key_para->pipe;
@@ -2459,6 +2503,11 @@
 		break;
 	}
 
+	if (qseecom.qsee.instance == qseecom.ce_drv.instance)
+		__qseecom_disable_clk(CLK_QSEE);
+	else
+		__qseecom_disable_clk(CLK_CE_DRV);
+
 	return ret;
 }
 
@@ -2877,18 +2926,43 @@
 		.release = qseecom_release
 };
 
-static int __qseecom_init_clk(void)
+static int __qseecom_init_clk(enum qseecom_ce_hw_instance ce)
 {
 	int rc = 0;
 	struct device *pdev;
 	struct qseecom_clk *qclk;
+	char *core_clk_src = NULL;
+	char *core_clk = NULL;
+	char *iface_clk = NULL;
+	char *bus_clk = NULL;
 
-	qclk = &qseecom.qsee;
-
+	switch (ce) {
+	case CLK_QSEE: {
+		core_clk_src = "core_clk_src";
+		core_clk = "core_clk";
+		iface_clk = "iface_clk";
+		bus_clk = "bus_clk";
+		qclk = &qseecom.qsee;
+		qclk->instance = CLK_QSEE;
+		break;
+	};
+	case CLK_CE_DRV: {
+		core_clk_src = "ce_drv_core_clk_src";
+		core_clk = "ce_drv_core_clk";
+		iface_clk = "ce_drv_iface_clk";
+		bus_clk = "ce_drv_bus_clk";
+		qclk = &qseecom.ce_drv;
+		qclk->instance = CLK_CE_DRV;
+		break;
+	};
+	default:
+		pr_err("Invalid ce hw instance: %d!\n", ce);
+		return -EIO;
+	}
 	pdev = qseecom.pdev;
-	/* Get CE3 src core clk. */
 
-	qclk->ce_core_src_clk = clk_get(pdev, "core_clk_src");
+	/* Get CE3 src core clk. */
+	qclk->ce_core_src_clk = clk_get(pdev, core_clk_src);
 	if (!IS_ERR(qclk->ce_core_src_clk)) {
 		/* Set the core src clk @100Mhz */
 		rc = clk_set_rate(qclk->ce_core_src_clk, QSEE_CE_CLK_100MHZ);
@@ -2903,7 +2977,7 @@
 	}
 
 	/* Get CE core clk */
-	qclk->ce_core_clk = clk_get(pdev, "core_clk");
+	qclk->ce_core_clk = clk_get(pdev, core_clk);
 	if (IS_ERR(qclk->ce_core_clk)) {
 		rc = PTR_ERR(qclk->ce_core_clk);
 		pr_err("Unable to get CE core clk\n");
@@ -2913,7 +2987,7 @@
 	}
 
 	/* Get CE Interface clk */
-	qclk->ce_clk = clk_get(pdev, "iface_clk");
+	qclk->ce_clk = clk_get(pdev, iface_clk);
 	if (IS_ERR(qclk->ce_clk)) {
 		rc = PTR_ERR(qclk->ce_clk);
 		pr_err("Unable to get CE interface clk\n");
@@ -2924,7 +2998,7 @@
 	}
 
 	/* Get CE AXI clk */
-	qclk->ce_bus_clk = clk_get(pdev, "bus_clk");
+	qclk->ce_bus_clk = clk_get(pdev, bus_clk);
 	if (IS_ERR(qclk->ce_bus_clk)) {
 		rc = PTR_ERR(qclk->ce_bus_clk);
 		pr_err("Unable to get CE BUS interface clk\n");
@@ -2937,11 +3011,14 @@
 	return rc;
 }
 
-static void __qseecom_deinit_clk(void)
+static void __qseecom_deinit_clk(enum qseecom_ce_hw_instance ce)
 {
 	struct qseecom_clk *qclk;
 
-	qclk = &qseecom.qsee;
+	if (ce == CLK_QSEE)
+		qclk = &qseecom.qsee;
+	else
+		qclk = &qseecom.ce_drv;
 
 	if (qclk->ce_clk != NULL) {
 		clk_put(qclk->ce_clk);
@@ -2979,6 +3056,11 @@
 	qseecom.qsee.ce_core_src_clk = NULL;
 	qseecom.qsee.ce_bus_clk = NULL;
 
+	qseecom.ce_drv.ce_core_clk = NULL;
+	qseecom.ce_drv.ce_clk = NULL;
+	qseecom.ce_drv.ce_core_src_clk = NULL;
+	qseecom.ce_drv.ce_bus_clk = NULL;
+
 	rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
 	if (rc < 0) {
 		pr_err("alloc_chrdev_region failed %d\n", rc);
@@ -3053,6 +3135,7 @@
 
 	/* register client for bus scaling */
 	if (pdev->dev.of_node) {
+
 		if (of_property_read_u32((&pdev->dev)->of_node,
 				"qcom,disk-encrypt-pipe-pair",
 				&qseecom.ce_info.disk_encrypt_pipe)) {
@@ -3089,10 +3172,29 @@
 			qseecom.ce_info.hlos_ce_hw_instance);
 		}
 
-		ret = __qseecom_init_clk();
+		qseecom.qsee.instance = qseecom.ce_info.qsee_ce_hw_instance;
+		qseecom.ce_drv.instance = qseecom.ce_info.hlos_ce_hw_instance;
+
+		ret = __qseecom_init_clk(CLK_QSEE);
 		if (ret)
 			goto err;
 
+		if (qseecom.qsee.instance != qseecom.ce_drv.instance) {
+			ret = __qseecom_init_clk(CLK_CE_DRV);
+			if (ret) {
+				__qseecom_deinit_clk(CLK_QSEE);
+				goto err;
+			}
+		} else {
+			struct qseecom_clk *qclk;
+
+			qclk = &qseecom.qsee;
+			qseecom.ce_drv.ce_core_clk = qclk->ce_core_clk;
+			qseecom.ce_drv.ce_clk = qclk->ce_clk;
+			qseecom.ce_drv.ce_core_src_clk = qclk->ce_core_src_clk;
+			qseecom.ce_drv.ce_bus_clk = qclk->ce_bus_clk;
+		}
+
 		qseecom_platform_support = (struct msm_bus_scale_pdata *)
 						msm_bus_cl_get_pdata(pdev);
 		if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
@@ -3203,9 +3305,11 @@
 		msm_bus_scale_client_update_request(qseecom.qsee_perf_client,
 									0);
 	/* register client for bus scaling */
-	if (pdev->dev.of_node)
-		__qseecom_deinit_clk();
-
+	if (pdev->dev.of_node) {
+		__qseecom_deinit_clk(CLK_QSEE);
+		if (qseecom.qsee.instance != qseecom.ce_drv.instance)
+			__qseecom_deinit_clk(CLK_CE_DRV);
+	}
 	return ret;
 };