Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2 | * |
| 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 Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 15 | #include <asm/errno.h> |
| 16 | #include <asm/sizes.h> |
| 17 | #include <linux/mutex.h> |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 18 | #include <mach/msm_memtypes.h> |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 19 | #include "smd_private.h" |
| 20 | |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 21 | #if defined(CONFIG_ARCH_MSM8960) |
| 22 | #include "rpm_resources.h" |
| 23 | #endif |
| 24 | |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 25 | static struct mem_region_t { |
| 26 | u64 start; |
| 27 | u64 size; |
| 28 | /* reserved for future use */ |
| 29 | u64 num_partitions; |
| 30 | int state; |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 31 | int mask; |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 32 | struct mutex state_mutex; |
| 33 | } mem_regions[MAX_NR_REGIONS]; |
| 34 | |
| 35 | static unsigned int nr_mem_regions; |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 36 | |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 37 | enum { |
| 38 | STATE_POWER_DOWN = 0x0, |
Naveen Ramaraj | 2e96c9f | 2011-09-22 14:16:03 -0700 | [diff] [blame^] | 39 | STATE_ACTIVE = 0x2, |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 40 | STATE_DEFAULT = STATE_ACTIVE |
| 41 | }; |
| 42 | |
| 43 | static int default_mask = ~0x0; |
| 44 | |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 45 | /* Return the number of chipselects populated with a memory bank */ |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 46 | /* This is 7x30 only and will be re-implemented in the future */ |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 47 | |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 48 | #if defined(CONFIG_ARCH_MSM7X30) |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 49 | unsigned 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 Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 64 | #endif |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 65 | |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 66 | #if defined(CONFIG_ARCH_MSM8960) |
| 67 | static 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 | |
| 103 | static 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 | |
| 127 | no_change: |
| 128 | mutex_unlock(&mem_regions[id].state_mutex); |
| 129 | return -EINVAL; |
| 130 | } |
| 131 | #else |
| 132 | |
| 133 | static 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 | */ |
| 142 | int 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 Ramaraj | 2e96c9f | 2011-09-22 14:16:03 -0700 | [diff] [blame^] | 160 | |
| 161 | if (change != STATE_ACTIVE && change != STATE_POWER_DOWN) { |
| 162 | pr_info("requested state transition invalid\n"); |
| 163 | return 0; |
| 164 | } |
| 165 | |
Naveen Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 166 | if (!switch_memory_state(match, change)) |
| 167 | return size; |
| 168 | else |
| 169 | return 0; |
| 170 | } |
| 171 | |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 172 | unsigned int get_num_memory_banks(void) |
| 173 | { |
| 174 | return nr_mem_regions; |
| 175 | } |
| 176 | |
| 177 | unsigned int get_memory_bank_size(unsigned int id) |
| 178 | { |
| 179 | BUG_ON(id >= nr_mem_regions); |
| 180 | return mem_regions[id].size; |
| 181 | } |
| 182 | |
| 183 | unsigned int get_memory_bank_start(unsigned int id) |
| 184 | { |
| 185 | BUG_ON(id >= nr_mem_regions); |
| 186 | return mem_regions[id].start; |
| 187 | } |
| 188 | |
| 189 | int __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 Ramaraj | 50442d0 | 2011-09-08 16:07:19 -0700 | [diff] [blame] | 214 | mem_regions[nr_mem_regions].state = STATE_DEFAULT; |
| 215 | mem_regions[nr_mem_regions].mask = default_mask; |
Naveen Ramaraj | 76483ad | 2011-09-06 14:25:44 -0700 | [diff] [blame] | 216 | nr_mem_regions++; |
| 217 | } |
| 218 | } |
| 219 | pr_info("Found %d memory banks\n", nr_mem_regions); |
| 220 | return 0; |
| 221 | } |