blob: 70aaf4a7ea35b225ae18f4b5ba7e06b1c3e6e898 [file] [log] [blame]
Jack Cheungd01fd832012-01-13 22:59:57 -08001/* Copyright (c) 2010-2012, 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>
Naveen Ramarajbc958dd2011-11-11 16:54:09 -080017#include <asm/pgtable.h>
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070018#include <linux/mutex.h>
Naveen Ramarajbc958dd2011-11-11 16:54:09 -080019#include <linux/memory.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <mach/msm_memtypes.h>
Naveen Ramarajbc958dd2011-11-11 16:54:09 -080021#include <mach/socinfo.h>
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070022#include "smd_private.h"
23
Naveen Ramaraj50442d02011-09-08 16:07:19 -070024#if defined(CONFIG_ARCH_MSM8960)
25#include "rpm_resources.h"
26#endif
27
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070028static struct mem_region_t {
29 u64 start;
30 u64 size;
31 /* reserved for future use */
32 u64 num_partitions;
33 int state;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070034} mem_regions[MAX_NR_REGIONS];
35
Jack Cheungd01fd832012-01-13 22:59:57 -080036static struct mutex mem_regions_mutex;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070037static unsigned int nr_mem_regions;
Jack Cheungd01fd832012-01-13 22:59:57 -080038static int mem_regions_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
Naveen Ramaraj50442d02011-09-08 16:07:19 -070040enum {
41 STATE_POWER_DOWN = 0x0,
Naveen Ramaraj2e96c9f2011-09-22 14:16:03 -070042 STATE_ACTIVE = 0x2,
Naveen Ramaraj50442d02011-09-08 16:07:19 -070043 STATE_DEFAULT = STATE_ACTIVE
44};
45
46static int default_mask = ~0x0;
47
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048/* Return the number of chipselects populated with a memory bank */
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070049/* This is 7x30 only and will be re-implemented in the future */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070051#if defined(CONFIG_ARCH_MSM7X30)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052unsigned int get_num_populated_chipselects()
53{
54 /* Currently, Linux cannot determine the memory toplogy of a target */
55 /* This is a kludge until all this info is figured out from smem */
56
57 /* There is atleast one chipselect populated for hosting the 1st bank */
58 unsigned int num_chipselects = 1;
59 int i;
60 for (i = 0; i < meminfo.nr_banks; i++) {
61 struct membank *bank = &meminfo.bank[i];
62 if (bank->start == EBI1_PHYS_OFFSET)
63 num_chipselects++;
64 }
65 return num_chipselects;
66}
Naveen Ramaraj76483ad2011-09-06 14:25:44 -070067#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068
Larry Basselb8d61022012-02-24 10:49:45 -080069#if (defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_MSM8930)) \
70 && defined(CONFIG_ENABLE_DMM)
Naveen Ramaraj50442d02011-09-08 16:07:19 -070071static int rpm_change_memory_state(int retention_mask,
72 int active_mask)
73{
74 int ret;
75 struct msm_rpm_iv_pair cmd[2];
76 struct msm_rpm_iv_pair status[2];
77
78 cmd[0].id = MSM_RPM_ID_DDR_DMM_0;
79 cmd[1].id = MSM_RPM_ID_DDR_DMM_1;
80
81 status[0].id = MSM_RPM_STATUS_ID_DDR_DMM_0;
82 status[1].id = MSM_RPM_STATUS_ID_DDR_DMM_1;
83
84 cmd[0].value = retention_mask;
85 cmd[1].value = active_mask;
86
87 ret = msm_rpm_set(MSM_RPM_CTX_SET_0, cmd, 2);
88 if (ret < 0) {
89 pr_err("rpm set failed");
90 return -EINVAL;
91 }
92
93 ret = msm_rpm_get_status(status, 2);
94 if (ret < 0) {
95 pr_err("rpm status failed");
96 return -EINVAL;
97 }
98 if (status[0].value == retention_mask &&
99 status[1].value == active_mask)
100 return 0;
101 else {
102 pr_err("rpm failed to change memory state");
103 return -EINVAL;
104 }
105}
106
Jack Cheungd01fd832012-01-13 22:59:57 -0800107static int switch_memory_state(int mask, int new_state, int start_region,
108 int end_region)
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700109{
Jack Cheungd01fd832012-01-13 22:59:57 -0800110 int final_mask = 0;
111 int i;
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700112
Jack Cheungd01fd832012-01-13 22:59:57 -0800113 mutex_lock(&mem_regions_mutex);
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700114
Jack Cheungd01fd832012-01-13 22:59:57 -0800115 for (i = start_region; i <= end_region; i++) {
116 if (new_state == mem_regions[i].state)
117 goto no_change;
118 /* All region states must be the same to change them */
119 if (mem_regions[i].state != mem_regions[start_region].state)
120 goto no_change;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800121 }
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700122
Jack Cheungd01fd832012-01-13 22:59:57 -0800123 if (new_state == STATE_POWER_DOWN)
124 final_mask = mem_regions_mask & mask;
125 else if (new_state == STATE_ACTIVE)
126 final_mask = mem_regions_mask | ~mask;
127 else
128 goto no_change;
129
130 pr_info("request memory %d to %d state switch (%d->%d)\n",
131 start_region, end_region, mem_regions[start_region].state,
132 new_state);
133 if (rpm_change_memory_state(final_mask, final_mask) == 0) {
134 for (i = start_region; i <= end_region; i++)
135 mem_regions[i].state = new_state;
136 mem_regions_mask = final_mask;
137
138 pr_info("completed memory %d to %d state switch to %d\n",
139 start_region, end_region, new_state);
140 mutex_unlock(&mem_regions_mutex);
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700141 return 0;
142 }
143
Jack Cheungd01fd832012-01-13 22:59:57 -0800144 pr_err("failed memory %d to %d state switch (%d->%d)\n",
145 start_region, end_region, mem_regions[start_region].state,
146 new_state);
147
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700148no_change:
Jack Cheungd01fd832012-01-13 22:59:57 -0800149 mutex_unlock(&mem_regions_mutex);
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700150 return -EINVAL;
151}
152#else
153
Jack Cheungd01fd832012-01-13 22:59:57 -0800154static int switch_memory_state(int mask, int new_state, int start_region,
155 int end_region)
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700156{
157 return -EINVAL;
158}
159#endif
160
161/* The hotplug code expects the number of bytes that switched state successfully
162 * as the return value, so a return value of zero indicates an error
163*/
164int soc_change_memory_power(u64 start, u64 size, int change)
165{
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700166 int i = 0;
Jack Cheungd01fd832012-01-13 22:59:57 -0800167 int mask = default_mask;
168 u64 end = start + size;
169 int start_region = 0;
170 int end_region = 0;
Naveen Ramaraj2e96c9f2011-09-22 14:16:03 -0700171
172 if (change != STATE_ACTIVE && change != STATE_POWER_DOWN) {
173 pr_info("requested state transition invalid\n");
174 return 0;
175 }
Jack Cheungd01fd832012-01-13 22:59:57 -0800176 /* Find the memory regions that fall within the range */
177 for (i = 0; i < nr_mem_regions; i++) {
178 if (mem_regions[i].start <= start &&
179 mem_regions[i].start >=
180 mem_regions[start_region].start) {
181 start_region = i;
182 }
183 if (end <= mem_regions[i].start + mem_regions[i].size) {
184 end_region = i;
185 break;
186 }
187 }
Naveen Ramaraj2e96c9f2011-09-22 14:16:03 -0700188
Jack Cheungd01fd832012-01-13 22:59:57 -0800189 /* Set the bitmask for each region in the range */
190 for (i = start_region; i <= end_region; i++)
191 mask &= ~(0x1 << i);
192
193 if (!switch_memory_state(mask, change, start_region, end_region))
Naveen Ramaraj50442d02011-09-08 16:07:19 -0700194 return size;
195 else
196 return 0;
197}
198
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700199unsigned int get_num_memory_banks(void)
200{
201 return nr_mem_regions;
202}
203
204unsigned int get_memory_bank_size(unsigned int id)
205{
206 BUG_ON(id >= nr_mem_regions);
207 return mem_regions[id].size;
208}
209
210unsigned int get_memory_bank_start(unsigned int id)
211{
212 BUG_ON(id >= nr_mem_regions);
213 return mem_regions[id].start;
214}
215
216int __init meminfo_init(unsigned int type, unsigned int min_bank_size)
217{
Jack Cheungd01fd832012-01-13 22:59:57 -0800218 unsigned int i, j;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800219 unsigned long bank_size;
220 unsigned long bank_start;
Jack Cheungd01fd832012-01-13 22:59:57 -0800221 unsigned long region_size;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700222 struct smem_ram_ptable *ram_ptable;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800223 /* physical memory banks */
224 unsigned int nr_mem_banks = 0;
225 /* logical memory regions for dmm */
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700226 nr_mem_regions = 0;
227
228 ram_ptable = smem_alloc(SMEM_USABLE_RAM_PARTITION_TABLE,
229 sizeof(struct smem_ram_ptable));
230
231 if (!ram_ptable) {
232 pr_err("Could not read ram partition table\n");
233 return -EINVAL;
234 }
235
236 pr_info("meminfo_init: smem ram ptable found: ver: %d len: %d\n",
237 ram_ptable->version, ram_ptable->len);
238
239 for (i = 0; i < ram_ptable->len; i++) {
Jack Cheungd01fd832012-01-13 22:59:57 -0800240 /* A bank is valid only if is greater than min_bank_size. If
241 * non-valid memory (e.g. modem memory) became greater than
242 * min_bank_size, there is currently no way to differentiate.
243 */
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700244 if (ram_ptable->parts[i].type == type &&
245 ram_ptable->parts[i].size >= min_bank_size) {
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800246 bank_start = ram_ptable->parts[i].start;
247 bank_size = ram_ptable->parts[i].size;
Jack Cheungd01fd832012-01-13 22:59:57 -0800248 region_size = bank_size / NR_REGIONS_PER_BANK;
249
250 for (j = 0; j < NR_REGIONS_PER_BANK; j++) {
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800251 mem_regions[nr_mem_regions].start =
252 bank_start;
253 mem_regions[nr_mem_regions].size =
Jack Cheungd01fd832012-01-13 22:59:57 -0800254 region_size;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800255 mem_regions[nr_mem_regions].state =
256 STATE_DEFAULT;
Jack Cheungd01fd832012-01-13 22:59:57 -0800257 bank_start += region_size;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800258 nr_mem_regions++;
259 }
260 nr_mem_banks++;
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700261 }
262 }
Jack Cheungd01fd832012-01-13 22:59:57 -0800263 mutex_init(&mem_regions_mutex);
264 mem_regions_mask = default_mask;
Naveen Ramarajbc958dd2011-11-11 16:54:09 -0800265 pr_info("Found %d memory banks grouped into %d memory regions\n",
266 nr_mem_banks, nr_mem_regions);
Naveen Ramaraj76483ad2011-09-06 14:25:44 -0700267 return 0;
268}