msm: 8960: add support for changing memory power state

Add required RPM requests to toggle memory power state as
needed by DMM.

Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index a51513f..2c278a6 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -29,6 +29,7 @@
 unsigned int get_num_memory_banks(void);
 unsigned int get_memory_bank_size(unsigned int);
 unsigned int get_memory_bank_start(unsigned int);
+int soc_change_memory_power(u64, u64, int);
 
 enum {
 	MEMTYPE_NONE = -1,
diff --git a/arch/arm/mach-msm/memory_topology.c b/arch/arm/mach-msm/memory_topology.c
index 423d626..53c7bd3 100644
--- a/arch/arm/mach-msm/memory_topology.c
+++ b/arch/arm/mach-msm/memory_topology.c
@@ -18,6 +18,10 @@
 #include <mach/msm_memtypes.h>
 #include "smd_private.h"
 
+#if defined(CONFIG_ARCH_MSM8960)
+#include "rpm_resources.h"
+#endif
+
 
 static struct mem_region_t {
 	u64 start;
@@ -25,11 +29,20 @@
 	/* reserved for future use */
 	u64 num_partitions;
 	int state;
+	int mask;
 	struct mutex state_mutex;
 } mem_regions[MAX_NR_REGIONS];
 
 static unsigned int nr_mem_regions;
 
+enum {
+	STATE_POWER_DOWN = 0x0,
+	STATE_ACTIVE,
+	STATE_DEFAULT = STATE_ACTIVE
+};
+
+static int default_mask = ~0x0;
+
 /* Return the number of chipselects populated with a memory bank */
 /* This is 7x30 only and will be re-implemented in the future */
 
@@ -51,6 +64,106 @@
 }
 #endif
 
+#if defined(CONFIG_ARCH_MSM8960)
+static int rpm_change_memory_state(int retention_mask,
+					int active_mask)
+{
+	int ret;
+	struct msm_rpm_iv_pair cmd[2];
+	struct msm_rpm_iv_pair status[2];
+
+	cmd[0].id = MSM_RPM_ID_DDR_DMM_0;
+	cmd[1].id = MSM_RPM_ID_DDR_DMM_1;
+
+	status[0].id = MSM_RPM_STATUS_ID_DDR_DMM_0;
+	status[1].id = MSM_RPM_STATUS_ID_DDR_DMM_1;
+
+	cmd[0].value = retention_mask;
+	cmd[1].value = active_mask;
+
+	ret = msm_rpm_set(MSM_RPM_CTX_SET_0, cmd, 2);
+	if (ret < 0) {
+		pr_err("rpm set failed");
+		return -EINVAL;
+	}
+
+	ret = msm_rpm_get_status(status, 2);
+	if (ret < 0) {
+		pr_err("rpm status failed");
+		return -EINVAL;
+	}
+	if (status[0].value == retention_mask &&
+		status[1].value == active_mask)
+		return 0;
+	else {
+		pr_err("rpm failed to change memory state");
+		return -EINVAL;
+	}
+}
+
+static int switch_memory_state(int id, int new_state)
+{
+	int mask;
+	int disable_masks[MAX_NR_REGIONS] = { 0xFFFFFF00, 0xFFFF00FF,
+						0xFF00FFFF, 0x00FFFFFF };
+	mutex_lock(&mem_regions[id].state_mutex);
+
+	if (new_state == mem_regions[id].state)
+		goto no_change;
+
+	if (new_state == STATE_POWER_DOWN)
+		mask = mem_regions[id].mask & disable_masks[id];
+	else if (new_state == STATE_ACTIVE)
+		mask = mem_regions[id].mask | (~disable_masks[id]);
+
+	/* For now we only support Deep Power Down */
+	/* So set the active and retention states as the same */
+	if (rpm_change_memory_state(mask, mask) == 0) {
+		mem_regions[id].state = new_state;
+		mem_regions[id].mask = mask;
+		mutex_unlock(&mem_regions[id].state_mutex);
+		return 0;
+	}
+
+no_change:
+	mutex_unlock(&mem_regions[id].state_mutex);
+	return -EINVAL;
+}
+#else
+
+static int switch_memory_state(int id, int new_state)
+{
+	return -EINVAL;
+}
+#endif
+
+/* The hotplug code expects the number of bytes that switched state successfully
+ * as the return value, so a return value of zero indicates an error
+*/
+int soc_change_memory_power(u64 start, u64 size, int change)
+{
+
+	int i = 0;
+	int match = 0;
+
+	/* Find the memory region starting just below start */
+	for (i = 0; i < nr_mem_regions; i++) {
+		if (mem_regions[i].start <= start &&
+			mem_regions[i].start >= mem_regions[match].start) {
+				match = i;
+		}
+	}
+
+	if (start + size > mem_regions[match].start + mem_regions[match].size) {
+		pr_info("passed size exceeds size of memory bank\n");
+		return 0;
+	}
+	if (!switch_memory_state(match, change))
+		return size;
+	else
+		return 0;
+}
+
 unsigned int get_num_memory_banks(void)
 {
 	return nr_mem_regions;
@@ -93,6 +206,8 @@
 			mem_regions[nr_mem_regions].size =
 				ram_ptable->parts[i].size;
 			mutex_init(&mem_regions[nr_mem_regions].state_mutex);
+			mem_regions[nr_mem_regions].state = STATE_DEFAULT;
+			mem_regions[nr_mem_regions].mask = default_mask;
 			nr_mem_regions++;
 		}
 	}