blob: 4afb9bd2f2fc979939301e28ff54cc825754f804 [file] [log] [blame]
Olav Hauganab77b1b2012-02-28 09:19:22 -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#include <mach/msm_subsystem_map.h>
14#include <linux/memory_alloc.h>
15#include <linux/iommu.h>
Laura Abbott0577d7b2012-04-17 11:14:30 -070016#include <linux/platform_device.h>
Olav Haugan16cdb412012-03-27 13:02:17 -070017#include <linux/vmalloc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <asm/sizes.h>
19#include <asm/page.h>
20#include <linux/init.h>
21#include <mach/iommu.h>
22#include <mach/iommu_domains.h>
Laura Abbott9f4a8e62011-08-29 19:08:07 -070023#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024
Laura Abbotte956cce2011-10-25 13:33:20 -070025/* dummy 4k for overmapping */
26char iommu_dummy[2*PAGE_SIZE-4];
27
Laura Abbott0577d7b2012-04-17 11:14:30 -070028struct msm_iommu_domain_state {
29 struct msm_iommu_domain *domains;
30 int ndomains;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031};
32
Laura Abbott0577d7b2012-04-17 11:14:30 -070033static struct msm_iommu_domain_state domain_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
Laura Abbotte956cce2011-10-25 13:33:20 -070035int msm_iommu_map_extra(struct iommu_domain *domain,
36 unsigned long start_iova,
37 unsigned long size,
38 int cached)
39{
Olav Haugan16cdb412012-03-27 13:02:17 -070040 int i, ret = 0;
41 struct scatterlist *sglist;
42 unsigned int nrpages = PFN_ALIGN(size) >> PAGE_SHIFT;
43 struct page *dummy_page = phys_to_page(
44 PFN_ALIGN(virt_to_phys(iommu_dummy)));
Laura Abbotte956cce2011-10-25 13:33:20 -070045
Olav Haugan16cdb412012-03-27 13:02:17 -070046 sglist = vmalloc(sizeof(*sglist) * nrpages);
47 if (!sglist) {
48 ret = -ENOMEM;
49 goto err1;
Laura Abbotte956cce2011-10-25 13:33:20 -070050 }
51
Olav Haugan16cdb412012-03-27 13:02:17 -070052 sg_init_table(sglist, nrpages);
Laura Abbotte956cce2011-10-25 13:33:20 -070053
Olav Haugan16cdb412012-03-27 13:02:17 -070054 for (i = 0; i < nrpages; i++)
55 sg_set_page(&sglist[i], dummy_page, PAGE_SIZE, 0);
Laura Abbotte956cce2011-10-25 13:33:20 -070056
Olav Haugan16cdb412012-03-27 13:02:17 -070057 ret = iommu_map_range(domain, start_iova, sglist, size, cached);
58 if (ret) {
59 pr_err("%s: could not map extra %lx in domain %p\n",
60 __func__, start_iova, domain);
61 }
Laura Abbotte956cce2011-10-25 13:33:20 -070062
Olav Haugan16cdb412012-03-27 13:02:17 -070063 vfree(sglist);
64err1:
65 return ret;
Laura Abbotte956cce2011-10-25 13:33:20 -070066}
67
Laura Abbottd027fdb2012-04-17 16:22:24 -070068static int msm_iommu_map_iova_phys(struct iommu_domain *domain,
69 unsigned long iova,
70 unsigned long phys,
71 unsigned long size,
72 int cached)
73{
74 int ret;
75 struct scatterlist *sglist;
76
77 sglist = vmalloc(sizeof(*sglist));
78 if (!sglist) {
79 ret = -ENOMEM;
80 goto err1;
81 }
82
83 sg_init_table(sglist, 1);
84 sglist->length = size;
85 sglist->offset = 0;
86 sglist->dma_address = phys;
87
88 ret = iommu_map_range(domain, iova, sglist, size, cached);
89 if (ret) {
90 pr_err("%s: could not map extra %lx in domain %p\n",
91 __func__, iova, domain);
92 }
93
94 vfree(sglist);
95err1:
96 return ret;
97
98}
99
100int msm_iommu_map_contig_buffer(unsigned long phys,
101 unsigned int domain_no,
102 unsigned int partition_no,
103 unsigned long size,
104 unsigned long align,
105 unsigned long cached,
106 unsigned long *iova_val)
107{
108 unsigned long iova;
109 int ret;
110
111 if (size & (align - 1))
112 return -EINVAL;
113
114 iova = msm_allocate_iova_address(domain_no, partition_no, size, align);
115
116 if (!iova)
117 return -ENOMEM;
118
119 ret = msm_iommu_map_iova_phys(msm_get_iommu_domain(domain_no), iova,
120 phys, size, cached);
121
122 if (ret)
123 msm_free_iova_address(iova, domain_no, partition_no, size);
124 else
125 *iova_val = iova;
126
127 return ret;
128}
129
130void msm_iommu_unmap_contig_buffer(unsigned long iova,
131 unsigned int domain_no,
132 unsigned int partition_no,
133 unsigned long size)
134{
135 iommu_unmap_range(msm_get_iommu_domain(domain_no), iova, size);
136 msm_free_iova_address(iova, domain_no, partition_no, size);
137}
Laura Abbotte956cce2011-10-25 13:33:20 -0700138
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700139struct iommu_domain *msm_get_iommu_domain(int domain_num)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140{
Laura Abbott0577d7b2012-04-17 11:14:30 -0700141 if (domain_num >= 0 && domain_num < domain_state.ndomains)
142 return domain_state.domains[domain_num].domain;
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700143 else
144 return NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145}
146
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700147unsigned long msm_allocate_iova_address(unsigned int iommu_domain,
148 unsigned int partition_no,
149 unsigned long size,
150 unsigned long align)
151{
152 struct mem_pool *pool;
153 unsigned long iova;
154
Laura Abbott0577d7b2012-04-17 11:14:30 -0700155 if (iommu_domain >= domain_state.ndomains)
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700156 return 0;
157
Laura Abbott0577d7b2012-04-17 11:14:30 -0700158 if (partition_no >= domain_state.domains[iommu_domain].npools)
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700159 return 0;
160
Laura Abbott0577d7b2012-04-17 11:14:30 -0700161 pool = &domain_state.domains[iommu_domain].iova_pools[partition_no];
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700162
163 if (!pool->gpool)
164 return 0;
165
166 iova = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align));
167 if (iova)
168 pool->free -= size;
169
170 return iova;
171}
172
173void msm_free_iova_address(unsigned long iova,
174 unsigned int iommu_domain,
175 unsigned int partition_no,
176 unsigned long size)
177{
178 struct mem_pool *pool;
179
Laura Abbott0577d7b2012-04-17 11:14:30 -0700180 if (iommu_domain >= domain_state.ndomains) {
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700181 WARN(1, "Invalid domain %d\n", iommu_domain);
182 return;
183 }
184
Laura Abbott0577d7b2012-04-17 11:14:30 -0700185 if (partition_no >= domain_state.domains[iommu_domain].npools) {
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700186 WARN(1, "Invalid partition %d for domain %d\n",
187 partition_no, iommu_domain);
188 return;
189 }
190
Laura Abbott0577d7b2012-04-17 11:14:30 -0700191 pool = &domain_state.domains[iommu_domain].iova_pools[partition_no];
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700192
193 if (!pool)
194 return;
195
196 pool->free += size;
197 gen_pool_free(pool->gpool, iova, size);
198}
199
200int msm_use_iommu()
201{
Laura Abbott0577d7b2012-04-17 11:14:30 -0700202 /*
203 * If there are no domains, don't bother trying to use the iommu
204 */
205 return domain_state.ndomains && iommu_found();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206}
207
Laura Abbott0577d7b2012-04-17 11:14:30 -0700208static int __init iommu_domain_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209{
Laura Abbott0577d7b2012-04-17 11:14:30 -0700210 struct iommu_domains_pdata *p = pdev->dev.platform_data;
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700211 int i, j;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212
Laura Abbott0577d7b2012-04-17 11:14:30 -0700213 if (!p)
214 return -ENODEV;
215
216 domain_state.domains = p->domains;
217 domain_state.ndomains = p->ndomains;
218
219 for (i = 0; i < domain_state.ndomains; i++) {
220 domain_state.domains[i].domain = iommu_domain_alloc(
221 p->domain_alloc_flags);
222 if (!domain_state.domains[i].domain)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 continue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
Laura Abbott0577d7b2012-04-17 11:14:30 -0700225 for (j = 0; j < domain_state.domains[i].npools; j++) {
226 struct mem_pool *pool = &domain_state.domains[i].
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700227 iova_pools[j];
228 mutex_init(&pool->pool_mutex);
Olav Haugan2d191032012-02-28 09:46:31 -0800229 if (pool->size) {
230 pool->gpool = gen_pool_create(PAGE_SHIFT, -1);
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700231
Olav Haugan2d191032012-02-28 09:46:31 -0800232 if (!pool->gpool) {
233 pr_err("%s: could not allocate pool\n",
234 __func__);
235 pr_err("%s: domain %d iova space %d\n",
236 __func__, i, j);
237 continue;
238 }
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700239
Olav Haugan2d191032012-02-28 09:46:31 -0800240 if (gen_pool_add(pool->gpool, pool->paddr,
241 pool->size, -1)) {
242 pr_err("%s: could not add memory\n",
243 __func__);
244 pr_err("%s: domain %d pool %d\n",
245 __func__, i, j);
246 gen_pool_destroy(pool->gpool);
247 pool->gpool = NULL;
248 continue;
249 }
250 } else {
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700251 pool->gpool = NULL;
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700252 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 }
254 }
255
Laura Abbott0577d7b2012-04-17 11:14:30 -0700256 for (i = 0; i < p->nnames; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 int domain_idx;
258 struct device *ctx = msm_iommu_get_ctx(
Laura Abbott0577d7b2012-04-17 11:14:30 -0700259 p->domain_names[i].name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260
261 if (!ctx)
262 continue;
263
Laura Abbott0577d7b2012-04-17 11:14:30 -0700264 domain_idx = p->domain_names[i].domain;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265
Laura Abbott0577d7b2012-04-17 11:14:30 -0700266 if (!domain_state.domains[domain_idx].domain)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 continue;
268
Laura Abbott0577d7b2012-04-17 11:14:30 -0700269 if (iommu_attach_device(domain_state.domains[domain_idx].domain,
Laura Abbott9f4a8e62011-08-29 19:08:07 -0700270 ctx)) {
271 WARN(1, "%s: could not attach domain %d to context %s."
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 " iommu programming will not occur.\n",
273 __func__, domain_idx,
Laura Abbott0577d7b2012-04-17 11:14:30 -0700274 p->domain_names[i].name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 continue;
276 }
277 }
278
279 return 0;
280}
Laura Abbott0577d7b2012-04-17 11:14:30 -0700281
282static struct platform_driver iommu_domain_driver = {
283 .driver = {
284 .name = "iommu_domains",
285 .owner = THIS_MODULE
286 },
287};
288
289static int __init msm_subsystem_iommu_init(void)
290{
291 return platform_driver_probe(&iommu_domain_driver, iommu_domain_probe);
292}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293device_initcall(msm_subsystem_iommu_init);