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(®ion_ctrl_lock);
for (i = region_start; i <= region_end; i++) {
@@ -605,9 +635,11 @@
}
mutex_unlock(®ion_ctrl_lock);
+ ocmem_disable_core_clock();
return 0;
invalid_transition:
mutex_unlock(®ion_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(®ion_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)