blob: b9d9d3be218232bf79ffbd03d75e996606cebe1f [file] [log] [blame]
Naveen Ramaraj76483ad2011-09-06 14:25:44 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13
14#include <asm/setup.h>
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070015#include <asm/errno.h>
16#include <asm/sizes.h>
17#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <mach/msm_memtypes.h>
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070019#include "smd_private.h"
20
Naveen Ramaraj50442d02011-09-08 16:07:19 -070021#if defined(CONFIG_ARCH_MSM8960)
22#include "rpm_resources.h"
23#endif
24
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070025static struct mem_region_t {
26 u64 start;
27 u64 size;
28 /* reserved for future use */
29 u64 num_partitions;
30 int state;
Naveen Ramaraj50442d02011-09-08 16:07:19 -070031 int mask;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070032 struct mutex state_mutex;
33} mem_regions[MAX_NR_REGIONS];
34
35static unsigned int nr_mem_regions;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
Naveen Ramaraj50442d02011-09-08 16:07:19 -070037enum {
38 STATE_POWER_DOWN = 0x0,
Naveen Ramaraj2e96c9f2011-09-22 14:16:03 -070039 STATE_ACTIVE = 0x2,
Naveen Ramaraj50442d02011-09-08 16:07:19 -070040 STATE_DEFAULT = STATE_ACTIVE
41};
42
43static int default_mask = ~0x0;
44
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045/* Return the number of chipselects populated with a memory bank */
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070046/* This is 7x30 only and will be re-implemented in the future */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070048#if defined(CONFIG_ARCH_MSM7X30)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049unsigned int get_num_populated_chipselects()
50{
51 /* Currently, Linux cannot determine the memory toplogy of a target */
52 /* This is a kludge until all this info is figured out from smem */
53
54 /* There is atleast one chipselect populated for hosting the 1st bank */
55 unsigned int num_chipselects = 1;
56 int i;
57 for (i = 0; i < meminfo.nr_banks; i++) {
58 struct membank *bank = &meminfo.bank[i];
59 if (bank->start == EBI1_PHYS_OFFSET)
60 num_chipselects++;
61 }
62 return num_chipselects;
63}
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070064#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
Naveen Ramaraj50442d02011-09-08 16:07:19 -070066#if defined(CONFIG_ARCH_MSM8960)
67static int rpm_change_memory_state(int retention_mask,
68 int active_mask)
69{
70 int ret;
71 struct msm_rpm_iv_pair cmd[2];
72 struct msm_rpm_iv_pair status[2];
73
74 cmd[0].id = MSM_RPM_ID_DDR_DMM_0;
75 cmd[1].id = MSM_RPM_ID_DDR_DMM_1;
76
77 status[0].id = MSM_RPM_STATUS_ID_DDR_DMM_0;
78 status[1].id = MSM_RPM_STATUS_ID_DDR_DMM_1;
79
80 cmd[0].value = retention_mask;
81 cmd[1].value = active_mask;
82
83 ret = msm_rpm_set(MSM_RPM_CTX_SET_0, cmd, 2);
84 if (ret < 0) {
85 pr_err("rpm set failed");
86 return -EINVAL;
87 }
88
89 ret = msm_rpm_get_status(status, 2);
90 if (ret < 0) {
91 pr_err("rpm status failed");
92 return -EINVAL;
93 }
94 if (status[0].value == retention_mask &&
95 status[1].value == active_mask)
96 return 0;
97 else {
98 pr_err("rpm failed to change memory state");
99 return -EINVAL;
100 }
101}
102
103static int switch_memory_state(int id, int new_state)
104{
105 int mask;
106 int disable_masks[MAX_NR_REGIONS] = { 0xFFFFFF00, 0xFFFF00FF,
107 0xFF00FFFF, 0x00FFFFFF };
108 mutex_lock(&mem_regions[id].state_mutex);
109
110 if (new_state == mem_regions[id].state)
111 goto no_change;
112
113 if (new_state == STATE_POWER_DOWN)
114 mask = mem_regions[id].mask & disable_masks[id];
115 else if (new_state == STATE_ACTIVE)
116 mask = mem_regions[id].mask | (~disable_masks[id]);
117
118 /* For now we only support Deep Power Down */
119 /* So set the active and retention states as the same */
120 if (rpm_change_memory_state(mask, mask) == 0) {
121 mem_regions[id].state = new_state;
122 mem_regions[id].mask = mask;
123 mutex_unlock(&mem_regions[id].state_mutex);
124 return 0;
125 }
126
127no_change:
128 mutex_unlock(&mem_regions[id].state_mutex);
129 return -EINVAL;
130}
131#else
132
133static int switch_memory_state(int id, int new_state)
134{
135 return -EINVAL;
136}
137#endif
138
139/* The hotplug code expects the number of bytes that switched state successfully
140 * as the return value, so a return value of zero indicates an error
141*/
142int soc_change_memory_power(u64 start, u64 size, int change)
143{
144
145 int i = 0;
146 int match = 0;
147
148 /* Find the memory region starting just below start */
149 for (i = 0; i < nr_mem_regions; i++) {
150 if (mem_regions[i].start <= start &&
151 mem_regions[i].start >= mem_regions[match].start) {
152 match = i;
153 }
154 }
155
156 if (start + size > mem_regions[match].start + mem_regions[match].size) {
157 pr_info("passed size exceeds size of memory bank\n");
158 return 0;
159 }
Naveen Ramaraj2e96c9f2011-09-22 14:16:03 -0700160
161 if (change != STATE_ACTIVE && change != STATE_POWER_DOWN) {
162 pr_info("requested state transition invalid\n");
163 return 0;
164 }
165
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700166 if (!switch_memory_state(match, change))
167 return size;
168 else
169 return 0;
170}
171
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700172unsigned int get_num_memory_banks(void)
173{
174 return nr_mem_regions;
175}
176
177unsigned int get_memory_bank_size(unsigned int id)
178{
179 BUG_ON(id >= nr_mem_regions);
180 return mem_regions[id].size;
181}
182
183unsigned int get_memory_bank_start(unsigned int id)
184{
185 BUG_ON(id >= nr_mem_regions);
186 return mem_regions[id].start;
187}
188
189int __init meminfo_init(unsigned int type, unsigned int min_bank_size)
190{
191 unsigned int i;
192 struct smem_ram_ptable *ram_ptable;
193 nr_mem_regions = 0;
194
195 ram_ptable = smem_alloc(SMEM_USABLE_RAM_PARTITION_TABLE,
196 sizeof(struct smem_ram_ptable));
197
198 if (!ram_ptable) {
199 pr_err("Could not read ram partition table\n");
200 return -EINVAL;
201 }
202
203 pr_info("meminfo_init: smem ram ptable found: ver: %d len: %d\n",
204 ram_ptable->version, ram_ptable->len);
205
206 for (i = 0; i < ram_ptable->len; i++) {
207 if (ram_ptable->parts[i].type == type &&
208 ram_ptable->parts[i].size >= min_bank_size) {
209 mem_regions[nr_mem_regions].start =
210 ram_ptable->parts[i].start;
211 mem_regions[nr_mem_regions].size =
212 ram_ptable->parts[i].size;
213 mutex_init(&mem_regions[nr_mem_regions].state_mutex);
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700214 mem_regions[nr_mem_regions].state = STATE_DEFAULT;
215 mem_regions[nr_mem_regions].mask = default_mask;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700216 nr_mem_regions++;
217 }
218 }
219 pr_info("Found %d memory banks\n", nr_mem_regions);
220 return 0;
221}