msm: gdsc: Collapse core memories during GDSC collapse on 8974

When a core is power-collapsed the contents of its internal
memories need not be retained. Collapse the memories to
save some additional power.

Change-Id: I1f9fd9a54017d24069d007b531b0a9706415a8b5
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index f9376f0..2f52fb0 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5121,6 +5121,17 @@
 	CLK_LOOKUP("bus_clk",  venus0_axi_clk.c, "fdc00000.qcom,vidc"),
 	CLK_LOOKUP("mem_clk",  venus0_ocmemnoc_clk.c, "fdc00000.qcom,vidc"),
 
+	CLK_LOOKUP("core_clk", venus0_vcodec0_clk.c, "fd8c1024.qcom,gdsc"),
+	CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd8c2304.qcom,gdsc"),
+	CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd8c2304.qcom,gdsc"),
+	CLK_LOOKUP("core0_clk", camss_jpeg_jpeg0_clk.c, "fd8c35a4.qcom,gdsc"),
+	CLK_LOOKUP("core1_clk", camss_jpeg_jpeg1_clk.c, "fd8c35a4.qcom,gdsc"),
+	CLK_LOOKUP("core2_clk", camss_jpeg_jpeg2_clk.c, "fd8c35a4.qcom,gdsc"),
+	CLK_LOOKUP("core0_clk", camss_vfe_vfe0_clk.c,	"fd8c36a4.qcom,gdsc"),
+	CLK_LOOKUP("core1_clk", camss_vfe_vfe1_clk.c,	"fd8c36a4.qcom,gdsc"),
+	CLK_LOOKUP("csi0_clk", camss_csi_vfe0_clk.c,	"fd8c36a4.qcom,gdsc"),
+	CLK_LOOKUP("csi1_clk", camss_csi_vfe1_clk.c,	"fd8c36a4.qcom,gdsc"),
+	CLK_LOOKUP("cpp_clk", camss_vfe_cpp_clk.c,	"fd8c36a4.qcom,gdsc"),
 	CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, "fd8c4024.qcom,gdsc"),
 
 	/* LPASS clocks */
diff --git a/arch/arm/mach-msm/gdsc.c b/arch/arm/mach-msm/gdsc.c
index 6665d66..30a034e 100644
--- a/arch/arm/mach-msm/gdsc.c
+++ b/arch/arm/mach-msm/gdsc.c
@@ -22,6 +22,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
 #include <linux/clk.h>
 #include <mach/clk.h>
 
@@ -44,7 +45,9 @@
 	struct regulator_dev	*rdev;
 	struct regulator_desc	rdesc;
 	void __iomem		*gdscr;
-	struct clk		*core_clk;
+	struct clk		**clocks;
+	int			clock_count;
+	bool			toggle_mems;
 };
 
 static int gdsc_is_enabled(struct regulator_dev *rdev)
@@ -58,7 +61,7 @@
 {
 	struct gdsc *sc = rdev_get_drvdata(rdev);
 	uint32_t regval;
-	int ret;
+	int i, ret;
 
 	regval = readl_relaxed(sc->gdscr);
 	regval &= ~SW_COLLAPSE_MASK;
@@ -71,9 +74,18 @@
 		return ret;
 	}
 
+	if (sc->toggle_mems) {
+		for (i = 0; i < sc->clock_count; i++) {
+			clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+			clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+		}
+	}
+
 	/*
 	 * If clocks to this power domain were already on, they will take an
 	 * additional 4 clock cycles to re-enable after the rail is enabled.
+	 * Delay to account for this. A delay is also needed to ensure clocks
+	 * are not enabled within 400ns of enabling power to the memories.
 	 */
 	udelay(1);
 
@@ -84,7 +96,7 @@
 {
 	struct gdsc *sc = rdev_get_drvdata(rdev);
 	uint32_t regval;
-	int ret;
+	int i, ret;
 
 	regval = readl_relaxed(sc->gdscr);
 	regval |= SW_COLLAPSE_MASK;
@@ -95,6 +107,13 @@
 	if (ret)
 		dev_err(&rdev->dev, "%s disable timed out\n", sc->rdesc.name);
 
+	if (sc->toggle_mems) {
+		for (i = 0; i < sc->clock_count; i++) {
+			clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+			clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+		}
+	}
+
 	return ret;
 }
 
@@ -112,7 +131,7 @@
 	struct gdsc *sc;
 	uint32_t regval;
 	bool retain_mems;
-	int ret;
+	int i, ret;
 
 	sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL);
 	if (sc == NULL)
@@ -137,6 +156,34 @@
 	if (sc->gdscr == NULL)
 		return -ENOMEM;
 
+	sc->clock_count = of_property_count_strings(pdev->dev.of_node,
+					    "qcom,clock-names");
+	if (sc->clock_count == -EINVAL) {
+		sc->clock_count = 0;
+	} else if (IS_ERR_VALUE(sc->clock_count)) {
+		dev_err(&pdev->dev, "Failed to get clock names\n");
+		return -EINVAL;
+	}
+
+	sc->clocks = devm_kzalloc(&pdev->dev,
+			sizeof(struct clk *) * sc->clock_count, GFP_KERNEL);
+	if (!sc->clocks)
+		return -ENOMEM;
+	for (i = 0; i < sc->clock_count; i++) {
+		const char *clock_name;
+		of_property_read_string_index(pdev->dev.of_node,
+					      "qcom,clock-names", i,
+					      &clock_name);
+		sc->clocks[i] = devm_clk_get(&pdev->dev, clock_name);
+		if (IS_ERR(sc->clocks[i])) {
+			int rc = PTR_ERR(sc->clocks[i]);
+			if (rc != -EPROBE_DEFER)
+				dev_err(&pdev->dev, "Failed to get %s\n",
+					clock_name);
+			return rc;
+		}
+	}
+
 	sc->rdesc.id = atomic_inc_return(&gdsc_count);
 	sc->rdesc.ops = &gdsc_ops;
 	sc->rdesc.type = REGULATOR_VOLTAGE;
@@ -157,13 +204,16 @@
 
 	retain_mems = of_property_read_bool(pdev->dev.of_node,
 					    "qcom,retain-mems");
-	if (retain_mems) {
-		sc->core_clk = devm_clk_get(&pdev->dev, "core_clk");
-		if (IS_ERR(sc->core_clk))
-			return PTR_ERR(sc->core_clk);
-		clk_set_flags(sc->core_clk, CLKFLAG_RETAIN_MEM);
-		clk_set_flags(sc->core_clk, CLKFLAG_RETAIN_PERIPH);
+	for (i = 0; i < sc->clock_count; i++) {
+		if (retain_mems || (regval & PWR_ON_MASK)) {
+			clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+			clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+		} else {
+			clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+			clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+		}
 	}
+	sc->toggle_mems = !retain_mems;
 
 	sc->rdev = regulator_register(&sc->rdesc, &pdev->dev, init_data, sc,
 				      pdev->dev.of_node);