msm: ocmem: Add clock management support

Manage the OCMEM core and branch clocks and keep them
on only when required to minimize the power impact.

Change-Id: I33339b317c1ec76af27bae56c552cb50fef3da0c
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 44fdc48..e4bcfa9 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5007,6 +5007,8 @@
 	CLK_LOOKUP("core_clk", oxilicx_axi_clk.c, "fdb10000.qcom,iommu"),
 	CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, "fdb10000.qcom,iommu"),
 	CLK_LOOKUP("alt_core_clk", oxili_gfx3d_clk.c, "fdb10000.qcom,iommu"),
+	CLK_LOOKUP("core_clk", ocmemgx_core_clk.c, "fdd00000.qcom,ocmem"),
+	CLK_LOOKUP("iface_clk", ocmemcx_ocmemnoc_clk.c, "fdd00000.qcom,ocmem"),
 	CLK_LOOKUP("iface_clk", venus0_ahb_clk.c, "fdc84000.qcom,iommu"),
 	CLK_LOOKUP("alt_core_clk", venus0_vcodec0_clk.c, "fdc84000.qcom,iommu"),
 	CLK_LOOKUP("core_clk", venus0_axi_clk.c, "fdc84000.qcom,iommu"),
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index e20e768..a5b0275 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -17,6 +17,7 @@
  *  Client drivers should use wrappers available in ocmem.h
  **/
 #include <linux/platform_device.h>
+#include <linux/clk.h>
 #include <asm/io.h>
 #include <mach/msm_iomap.h>
 #include "ocmem.h"
@@ -74,6 +75,8 @@
 	void __iomem *vbase;
 	unsigned long size;
 	unsigned long base;
+	struct clk *core_clk;
+	struct clk *iface_clk;
 	struct ocmem_partition *parts;
 	int nr_parts;
 	void __iomem *reg_base;
@@ -208,4 +211,8 @@
 unsigned long process_quota(int);
 int ocmem_memory_off(int, unsigned long, unsigned long);
 int ocmem_memory_on(int, unsigned long, unsigned long);
+int ocmem_enable_core_clock(void);
+int ocmem_enable_iface_clock(void);
+void ocmem_disable_core_clock(void);
+void ocmem_disable_iface_clock(void);
 #endif
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index b5754a7..82fe2f8 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -296,6 +296,44 @@
 }
 #endif /* CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL */
 
+/* Core Clock Operations */
+int ocmem_enable_core_clock(void)
+{
+	int ret;
+	ret = clk_prepare_enable(ocmem_pdata->core_clk);
+	if (ret) {
+		pr_err("ocmem: Failed to enable core clock\n");
+		return ret;
+	}
+	pr_debug("ocmem: Enabled core clock\n");
+	return 0;
+}
+
+void ocmem_disable_core_clock(void)
+{
+	clk_disable_unprepare(ocmem_pdata->core_clk);
+	pr_debug("ocmem: Disabled core clock\n");
+}
+
+/* Branch Clock Operations */
+int ocmem_enable_iface_clock(void)
+{
+	int ret;
+	ret = clk_prepare_enable(ocmem_pdata->iface_clk);
+	if (ret) {
+		pr_err("ocmem: Failed to disable branch clock\n");
+		return ret;
+	}
+	pr_debug("ocmem: Enabled iface clock\n");
+	return 0;
+}
+
+void ocmem_disable_iface_clock(void)
+{
+	clk_disable_unprepare(ocmem_pdata->iface_clk);
+	pr_debug("ocmem: Disabled iface clock\n");
+}
+
 static struct ocmem_plat_data *parse_dt_config(struct platform_device *pdev)
 {
 	struct device   *dev = &pdev->dev;
@@ -530,6 +568,7 @@
 /* This will be programmed by TZ after TZ support is integrated */
 static int ocmem_init_gfx_mpu(struct platform_device *pdev)
 {
+	int rc;
 	struct device *dev = &pdev->dev;
 	void __iomem *ocmem_region_vbase = NULL;
 
@@ -538,13 +577,21 @@
 	if (!ocmem_region_vbase)
 		return -EBUSY;
 
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0)
+		return rc;
+
 	writel_relaxed(GRAPHICS_REGION_CTL, ocmem_region_vbase + 0xFCC);
+	ocmem_disable_core_clock();
 	return 0;
 }
 
 static int __devinit msm_ocmem_probe(struct platform_device *pdev)
 {
 	struct device   *dev = &pdev->dev;
+	struct clk *ocmem_core_clk = NULL;
+	struct clk *ocmem_iface_clk = NULL;
 
 	if (!pdev->dev.of_node) {
 		dev_info(dev, "Missing Configuration in Device Tree\n");
@@ -563,6 +610,29 @@
 
 	dev_info(dev, "OCMEM Virtual addr %p\n", ocmem_pdata->vbase);
 
+	ocmem_core_clk = devm_clk_get(dev, "core_clk");
+
+	if (IS_ERR(ocmem_core_clk)) {
+		dev_err(dev, "Unable to get the core clock\n");
+		return PTR_ERR(ocmem_core_clk);
+	}
+
+	/* The core clock is synchronous with graphics */
+	if (clk_set_rate(ocmem_core_clk, 1000) < 0) {
+		dev_err(dev, "Set rate failed on the core clock\n");
+		return -EBUSY;
+	}
+
+	ocmem_iface_clk = devm_clk_get(dev, "iface_clk");
+
+	if (IS_ERR(ocmem_iface_clk)) {
+		dev_err(dev, "Unable to get the memory interface clock\n");
+		return PTR_ERR(ocmem_core_clk);
+	};
+
+	ocmem_pdata->core_clk = ocmem_core_clk;
+	ocmem_pdata->iface_clk = ocmem_iface_clk;
+
 	platform_set_drvdata(pdev, ocmem_pdata);
 
 	if (ocmem_core_init(pdev))
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index d8cfefc..c7cc57e 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -384,6 +384,22 @@
 }
 
 #if defined(CONFIG_MSM_OCMEM_POWER_DISABLE)
+static int ocmem_core_set_default_state(void)
+{
+	int rc = 0;
+
+	/* The OCMEM core clock and branch clocks are always turned ON */
+	rc = ocmem_enable_core_clock();
+	if (rc < 0)
+		return rc;
+
+	rc = ocmem_enable_iface_clock();
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
 /* Initializes a region to be turned ON in wide mode */
 static int ocmem_region_set_default_state(unsigned int r_num)
 {
@@ -408,6 +424,11 @@
 {
 	return 0;
 }
+
+static int ocmem_core_set_default_state(void)
+{
+	return 0;
+}
 #endif
 
 #if defined(CONFIG_MSM_OCMEM_POWER_DEBUG)
@@ -535,6 +556,7 @@
 	unsigned start_m = num_banks;
 	unsigned end_m = num_banks;
 	unsigned long region_offset = 0;
+	int rc = 0;
 
 	if (offset < 0)
 		return -EINVAL;
@@ -555,6 +577,14 @@
 		(region_end >= num_regions))
 			return -EINVAL;
 
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0) {
+		pr_err("ocmem: Power transistion request for client %s (id: %d) failed\n",
+				get_name(id), id);
+		return rc;
+	}
+
 	mutex_lock(&region_ctrl_lock);
 
 	for (i = region_start; i <= region_end; i++) {
@@ -605,9 +635,11 @@
 
 	}
 	mutex_unlock(&region_ctrl_lock);
+	ocmem_disable_core_clock();
 	return 0;
 invalid_transition:
 	mutex_unlock(&region_ctrl_lock);
+	ocmem_disable_core_clock();
 	pr_err("ocmem_core: Invalid state transition detected for %d\n", id);
 	pr_err("ocmem_core: Offset %lx Len %lx curr_state %x new_state %x\n",
 			offset, len, curr_state, new_state);
@@ -640,10 +672,16 @@
 	bool interleaved;
 	unsigned i, j, k;
 	unsigned rsc_type = 0;
+	int rc = 0;
 
 	pdata = platform_get_drvdata(pdev);
 	ocmem_base = pdata->reg_base;
 
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0)
+		return rc;
+
 	hw_ver = ocmem_read(ocmem_base + OC_HW_PROFILE);
 
 	if (pdata->nr_regions != OCMEM_V1_REGIONS) {
@@ -684,8 +722,7 @@
 					 * num_regions, GFP_KERNEL);
 
 	if (!region_ctrl) {
-		pr_err("ocmem: Unable to allocate memory\n");
-		return -EINVAL;
+		goto err_no_mem;
 	}
 
 	mutex_init(&region_ctrl_lock);
@@ -702,8 +739,7 @@
 					sizeof(struct ocmem_hw_macro) *
 						num_banks, GFP_KERNEL);
 		if (!region->macro) {
-			pr_err("ocmem: Unable to allocate memory\n");
-			return -EINVAL;
+			goto err_no_mem;
 		}
 
 		for (j = 0; j < num_banks; j++) {
@@ -722,7 +758,7 @@
 
 			if (!req) {
 				pr_err("Unable to create RPM request\n");
-				return -EINVAL;
+				goto region_init_error;
 			}
 
 			pr_debug("rpm request type %x (rsc: %d) with %d elements\n",
@@ -733,16 +769,28 @@
 
 		if (ocmem_region_toggle(i)) {
 			pr_err("Failed to verify region %d\n", i);
-			goto hw_not_supported;
+			goto region_init_error;
 		}
 
 		if (ocmem_region_set_default_state(i)) {
 			pr_err("Failed to initialize region %d\n", i);
-			goto hw_not_supported;
+			goto region_init_error;
 		}
 	}
+
+	rc = ocmem_core_set_default_state();
+
+	if (rc < 0)
+		return rc;
+
+	ocmem_disable_core_clock();
 	return 0;
+
+err_no_mem:
+	pr_err("ocmem: Unable to allocate memory\n");
+region_init_error:
 hw_not_supported:
 	pr_err("Unsupported OCMEM h/w configuration %x\n", hw_ver);
+	ocmem_disable_core_clock();
 	return -EINVAL;
 }
diff --git a/arch/arm/mach-msm/ocmem_rdm.c b/arch/arm/mach-msm/ocmem_rdm.c
index 5649021..ccbef9b 100644
--- a/arch/arm/mach-msm/ocmem_rdm.c
+++ b/arch/arm/mach-msm/ocmem_rdm.c
@@ -142,6 +142,15 @@
 	int i = 0;
 	int j = 0;
 	int status = 0;
+	int rc = 0;
+
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0) {
+		pr_err("RDM transfer failed for client %s (id: %d)\n",
+				get_name(id), id);
+		return rc;
+	}
 
 	for (i = 0, j = slot; i < num_chunks; i++, j++) {
 
@@ -196,6 +205,7 @@
 	wait_event_interruptible(dm_wq,
 		atomic_read(&dm_pending) == 0);
 
+	ocmem_disable_core_clock();
 	return 0;
 }
 
@@ -218,8 +228,16 @@
 		return -EINVAL;
 	}
 
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0) {
+		pr_err("RDM initialization failed\n");
+		return rc;
+	}
+
 	init_waitqueue_head(&dm_wq);
 	/* enable dm interrupts */
 	ocmem_write(DM_INTR_ENABLE, dm_base + DM_INTR_MASK);
+	ocmem_disable_core_clock();
 	return 0;
 }
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index 75081af..3ac8e0a 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -511,18 +511,48 @@
 	return 0;
 }
 
-/* process map is a wrapper where power control will be added later */
 static int process_map(struct ocmem_req *req, unsigned long start,
 				unsigned long end)
 {
+	int rc = 0;
+
+	rc = ocmem_enable_core_clock();
+
+	if (rc < 0)
+		goto core_clock_fail;
+
+	rc = ocmem_enable_iface_clock();
+
+	if (rc < 0)
+		goto process_map_fail;
+
 	return do_map(req);
+
+process_map_fail:
+	ocmem_disable_core_clock();
+core_clock_fail:
+	pr_err("ocmem: Failed to map ocmem request\n");
+	return rc;
 }
 
-/* process unmap is a wrapper where power control will be added later */
 static int process_unmap(struct ocmem_req *req, unsigned long start,
 				unsigned long end)
 {
-	return do_unmap(req);
+	int rc = 0;
+
+	rc = do_unmap(req);
+
+	if (rc < 0)
+		goto process_unmap_fail;
+
+	ocmem_disable_iface_clock();
+	ocmem_disable_core_clock();
+
+	return 0;
+
+process_unmap_fail:
+	pr_err("ocmem: Failed to unmap ocmem request\n");
+	return rc;
 }
 
 static int __sched_grow(struct ocmem_req *req, bool can_block)