Olav Haugan | ab77b1b | 2012-02-28 09:19:22 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2010-2012, 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 | #include <mach/msm_subsystem_map.h> |
| 14 | #include <linux/memory_alloc.h> |
| 15 | #include <linux/iommu.h> |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 16 | #include <linux/vmalloc.h> |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 17 | #include <asm/sizes.h> |
| 18 | #include <asm/page.h> |
| 19 | #include <linux/init.h> |
| 20 | #include <mach/iommu.h> |
| 21 | #include <mach/iommu_domains.h> |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 22 | #include <mach/socinfo.h> |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 23 | |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 24 | /* dummy 4k for overmapping */ |
| 25 | char iommu_dummy[2*PAGE_SIZE-4]; |
| 26 | |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 27 | struct msm_iommu_domain { |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 28 | /* iommu domain to map in */ |
| 29 | struct iommu_domain *domain; |
| 30 | /* total number of allocations from this domain */ |
| 31 | atomic_t allocation_cnt; |
| 32 | /* number of iova pools */ |
| 33 | int npools; |
| 34 | /* |
| 35 | * array of gen_pools for allocating iovas. |
| 36 | * behavior is undefined if these overlap |
| 37 | */ |
| 38 | struct mem_pool *iova_pools; |
| 39 | |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 40 | }; |
| 41 | |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 42 | |
| 43 | struct { |
| 44 | char *name; |
| 45 | int domain; |
| 46 | } msm_iommu_ctx_names[] = { |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 47 | /* Camera */ |
| 48 | { |
| 49 | .name = "vpe_src", |
| 50 | .domain = CAMERA_DOMAIN, |
| 51 | }, |
| 52 | /* Camera */ |
| 53 | { |
| 54 | .name = "vpe_dst", |
| 55 | .domain = CAMERA_DOMAIN, |
| 56 | }, |
| 57 | /* Camera */ |
| 58 | { |
| 59 | .name = "vfe_imgwr", |
| 60 | .domain = CAMERA_DOMAIN, |
| 61 | }, |
| 62 | /* Camera */ |
| 63 | { |
| 64 | .name = "vfe_misc", |
| 65 | .domain = CAMERA_DOMAIN, |
| 66 | }, |
| 67 | /* Camera */ |
| 68 | { |
| 69 | .name = "ijpeg_src", |
| 70 | .domain = CAMERA_DOMAIN, |
| 71 | }, |
| 72 | /* Camera */ |
| 73 | { |
| 74 | .name = "ijpeg_dst", |
| 75 | .domain = CAMERA_DOMAIN, |
| 76 | }, |
| 77 | /* Camera */ |
| 78 | { |
| 79 | .name = "jpegd_src", |
| 80 | .domain = CAMERA_DOMAIN, |
| 81 | }, |
| 82 | /* Camera */ |
| 83 | { |
| 84 | .name = "jpegd_dst", |
| 85 | .domain = CAMERA_DOMAIN, |
| 86 | }, |
| 87 | /* Rotator */ |
| 88 | { |
| 89 | .name = "rot_src", |
| 90 | .domain = ROTATOR_DOMAIN, |
| 91 | }, |
| 92 | /* Rotator */ |
| 93 | { |
| 94 | .name = "rot_dst", |
| 95 | .domain = ROTATOR_DOMAIN, |
| 96 | }, |
| 97 | /* Video */ |
| 98 | { |
| 99 | .name = "vcodec_a_mm1", |
| 100 | .domain = VIDEO_DOMAIN, |
| 101 | }, |
| 102 | /* Video */ |
| 103 | { |
| 104 | .name = "vcodec_b_mm2", |
| 105 | .domain = VIDEO_DOMAIN, |
| 106 | }, |
| 107 | /* Video */ |
| 108 | { |
| 109 | .name = "vcodec_a_stream", |
| 110 | .domain = VIDEO_DOMAIN, |
| 111 | }, |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 112 | }; |
| 113 | |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 114 | static struct mem_pool video_pools[] = { |
| 115 | /* |
| 116 | * Video hardware has the following requirements: |
| 117 | * 1. All video addresses used by the video hardware must be at a higher |
| 118 | * address than video firmware address. |
| 119 | * 2. Video hardware can only access a range of 256MB from the base of |
| 120 | * the video firmware. |
| 121 | */ |
| 122 | [VIDEO_FIRMWARE_POOL] = |
| 123 | /* Low addresses, intended for video firmware */ |
| 124 | { |
| 125 | .paddr = SZ_128K, |
| 126 | .size = SZ_16M - SZ_128K, |
| 127 | }, |
| 128 | [VIDEO_MAIN_POOL] = |
| 129 | /* Main video pool */ |
| 130 | { |
| 131 | .paddr = SZ_16M, |
| 132 | .size = SZ_256M - SZ_16M, |
| 133 | }, |
| 134 | [GEN_POOL] = |
| 135 | /* Remaining address space up to 2G */ |
| 136 | { |
| 137 | .paddr = SZ_256M, |
| 138 | .size = SZ_2G - SZ_256M, |
| 139 | }, |
| 140 | }; |
| 141 | |
| 142 | static struct mem_pool camera_pools[] = { |
| 143 | [GEN_POOL] = |
| 144 | /* One address space for camera */ |
| 145 | { |
| 146 | .paddr = SZ_128K, |
| 147 | .size = SZ_2G - SZ_128K, |
| 148 | }, |
| 149 | }; |
| 150 | |
| 151 | static struct mem_pool display_pools[] = { |
| 152 | [GEN_POOL] = |
| 153 | /* One address space for display */ |
| 154 | { |
| 155 | .paddr = SZ_128K, |
| 156 | .size = SZ_2G - SZ_128K, |
| 157 | }, |
| 158 | }; |
| 159 | |
| 160 | static struct mem_pool rotator_pools[] = { |
| 161 | [GEN_POOL] = |
| 162 | /* One address space for rotator */ |
| 163 | { |
| 164 | .paddr = SZ_128K, |
| 165 | .size = SZ_2G - SZ_128K, |
| 166 | }, |
| 167 | }; |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 168 | |
| 169 | static struct msm_iommu_domain msm_iommu_domains[] = { |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 170 | [VIDEO_DOMAIN] = { |
| 171 | .iova_pools = video_pools, |
| 172 | .npools = ARRAY_SIZE(video_pools), |
| 173 | }, |
| 174 | [CAMERA_DOMAIN] = { |
| 175 | .iova_pools = camera_pools, |
| 176 | .npools = ARRAY_SIZE(camera_pools), |
| 177 | }, |
| 178 | [DISPLAY_DOMAIN] = { |
| 179 | .iova_pools = display_pools, |
| 180 | .npools = ARRAY_SIZE(display_pools), |
| 181 | }, |
| 182 | [ROTATOR_DOMAIN] = { |
| 183 | .iova_pools = rotator_pools, |
| 184 | .npools = ARRAY_SIZE(rotator_pools), |
| 185 | }, |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 186 | }; |
| 187 | |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 188 | int msm_iommu_map_extra(struct iommu_domain *domain, |
| 189 | unsigned long start_iova, |
| 190 | unsigned long size, |
| 191 | int cached) |
| 192 | { |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 193 | int i, ret = 0; |
| 194 | struct scatterlist *sglist; |
| 195 | unsigned int nrpages = PFN_ALIGN(size) >> PAGE_SHIFT; |
| 196 | struct page *dummy_page = phys_to_page( |
| 197 | PFN_ALIGN(virt_to_phys(iommu_dummy))); |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 198 | |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 199 | sglist = vmalloc(sizeof(*sglist) * nrpages); |
| 200 | if (!sglist) { |
| 201 | ret = -ENOMEM; |
| 202 | goto err1; |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 203 | } |
| 204 | |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 205 | sg_init_table(sglist, nrpages); |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 206 | |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 207 | for (i = 0; i < nrpages; i++) |
| 208 | sg_set_page(&sglist[i], dummy_page, PAGE_SIZE, 0); |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 209 | |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 210 | ret = iommu_map_range(domain, start_iova, sglist, size, cached); |
| 211 | if (ret) { |
| 212 | pr_err("%s: could not map extra %lx in domain %p\n", |
| 213 | __func__, start_iova, domain); |
| 214 | } |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 215 | |
Olav Haugan | 16cdb41 | 2012-03-27 13:02:17 -0700 | [diff] [blame] | 216 | vfree(sglist); |
| 217 | err1: |
| 218 | return ret; |
Laura Abbott | e956cce | 2011-10-25 13:33:20 -0700 | [diff] [blame] | 219 | } |
| 220 | |
| 221 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 222 | struct iommu_domain *msm_get_iommu_domain(int domain_num) |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 223 | { |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 224 | if (domain_num >= 0 && domain_num < MAX_DOMAINS) |
| 225 | return msm_iommu_domains[domain_num].domain; |
| 226 | else |
| 227 | return NULL; |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 228 | } |
| 229 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 230 | unsigned long msm_allocate_iova_address(unsigned int iommu_domain, |
| 231 | unsigned int partition_no, |
| 232 | unsigned long size, |
| 233 | unsigned long align) |
| 234 | { |
| 235 | struct mem_pool *pool; |
| 236 | unsigned long iova; |
| 237 | |
| 238 | if (iommu_domain >= MAX_DOMAINS) |
| 239 | return 0; |
| 240 | |
| 241 | if (partition_no >= msm_iommu_domains[iommu_domain].npools) |
| 242 | return 0; |
| 243 | |
| 244 | pool = &msm_iommu_domains[iommu_domain].iova_pools[partition_no]; |
| 245 | |
| 246 | if (!pool->gpool) |
| 247 | return 0; |
| 248 | |
| 249 | iova = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align)); |
| 250 | if (iova) |
| 251 | pool->free -= size; |
| 252 | |
| 253 | return iova; |
| 254 | } |
| 255 | |
| 256 | void msm_free_iova_address(unsigned long iova, |
| 257 | unsigned int iommu_domain, |
| 258 | unsigned int partition_no, |
| 259 | unsigned long size) |
| 260 | { |
| 261 | struct mem_pool *pool; |
| 262 | |
| 263 | if (iommu_domain >= MAX_DOMAINS) { |
| 264 | WARN(1, "Invalid domain %d\n", iommu_domain); |
| 265 | return; |
| 266 | } |
| 267 | |
| 268 | if (partition_no >= msm_iommu_domains[iommu_domain].npools) { |
| 269 | WARN(1, "Invalid partition %d for domain %d\n", |
| 270 | partition_no, iommu_domain); |
| 271 | return; |
| 272 | } |
| 273 | |
| 274 | pool = &msm_iommu_domains[iommu_domain].iova_pools[partition_no]; |
| 275 | |
| 276 | if (!pool) |
| 277 | return; |
| 278 | |
| 279 | pool->free += size; |
| 280 | gen_pool_free(pool->gpool, iova, size); |
| 281 | } |
| 282 | |
| 283 | int msm_use_iommu() |
| 284 | { |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 285 | return iommu_found(); |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | static int __init msm_subsystem_iommu_init(void) |
| 289 | { |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 290 | int i, j; |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 291 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 292 | for (i = 0; i < ARRAY_SIZE(msm_iommu_domains); i++) { |
| 293 | msm_iommu_domains[i].domain = iommu_domain_alloc(0); |
| 294 | if (!msm_iommu_domains[i].domain) |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 295 | continue; |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 296 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 297 | for (j = 0; j < msm_iommu_domains[i].npools; j++) { |
| 298 | struct mem_pool *pool = &msm_iommu_domains[i]. |
| 299 | iova_pools[j]; |
| 300 | mutex_init(&pool->pool_mutex); |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 301 | if (pool->size) { |
| 302 | pool->gpool = gen_pool_create(PAGE_SHIFT, -1); |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 303 | |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 304 | if (!pool->gpool) { |
| 305 | pr_err("%s: could not allocate pool\n", |
| 306 | __func__); |
| 307 | pr_err("%s: domain %d iova space %d\n", |
| 308 | __func__, i, j); |
| 309 | continue; |
| 310 | } |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 311 | |
Olav Haugan | 2d19103 | 2012-02-28 09:46:31 -0800 | [diff] [blame] | 312 | if (gen_pool_add(pool->gpool, pool->paddr, |
| 313 | pool->size, -1)) { |
| 314 | pr_err("%s: could not add memory\n", |
| 315 | __func__); |
| 316 | pr_err("%s: domain %d pool %d\n", |
| 317 | __func__, i, j); |
| 318 | gen_pool_destroy(pool->gpool); |
| 319 | pool->gpool = NULL; |
| 320 | continue; |
| 321 | } |
| 322 | } else { |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 323 | pool->gpool = NULL; |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 324 | } |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 325 | } |
| 326 | } |
| 327 | |
| 328 | for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) { |
| 329 | int domain_idx; |
| 330 | struct device *ctx = msm_iommu_get_ctx( |
| 331 | msm_iommu_ctx_names[i].name); |
| 332 | |
| 333 | if (!ctx) |
| 334 | continue; |
| 335 | |
| 336 | domain_idx = msm_iommu_ctx_names[i].domain; |
| 337 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 338 | if (!msm_iommu_domains[domain_idx].domain) |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 339 | continue; |
| 340 | |
Laura Abbott | 9f4a8e6 | 2011-08-29 19:08:07 -0700 | [diff] [blame] | 341 | if (iommu_attach_device(msm_iommu_domains[domain_idx].domain, |
| 342 | ctx)) { |
| 343 | WARN(1, "%s: could not attach domain %d to context %s." |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 344 | " iommu programming will not occur.\n", |
| 345 | __func__, domain_idx, |
| 346 | msm_iommu_ctx_names[i].name); |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 347 | continue; |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | return 0; |
| 352 | } |
| 353 | device_initcall(msm_subsystem_iommu_init); |