blob: 194067bcda1b7a8e8a85bacd90c04e22cf937bc5 [file] [log] [blame]
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
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 <linux/types.h>
14#include <linux/device.h>
15#include <linux/spinlock.h>
16#include <linux/genalloc.h>
17#include <linux/slab.h>
18#include <linux/iommu.h>
19#include <mach/iommu.h>
20#include <linux/msm_kgsl.h>
21
22#include "kgsl.h"
23#include "kgsl_device.h"
24#include "kgsl_mmu.h"
25#include "kgsl_sharedmem.h"
26
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070027/*
28 * On APQ8064, KGSL can control a maximum of 4 IOMMU devices: 2 user and 2
29 * priv domains, 1 each for each of the AXI ports attached to the GPU. 8660
30 * and 8960 have only one AXI port, so maximum allowable IOMMU devices for those
31 * chips is 2.
32 */
33
34#define KGSL_IOMMU_MAX_DEV 4
35
36struct kgsl_iommu_device {
37 struct device *dev;
38 int attached;
39};
40
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060041struct kgsl_iommu {
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070042 struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEV];
43 int dev_count;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060044};
45
46static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
47 unsigned int pt_base)
48{
49 struct iommu_domain *domain = pt->priv;
50 return pt && pt_base && ((unsigned int)domain == pt_base);
51}
52
53static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
54{
55 struct iommu_domain *domain = mmu_specific_pt;
56 if (domain)
57 iommu_domain_free(domain);
58}
59
60void *kgsl_iommu_create_pagetable(void)
61{
62 struct iommu_domain *domain = iommu_domain_alloc(0);
63 if (!domain)
64 KGSL_CORE_ERR("Failed to create iommu domain\n");
65
66 return domain;
67}
68
69static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
70{
71 struct iommu_domain *domain;
72 struct kgsl_iommu *iommu = mmu->priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070073 int i;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060074
75 BUG_ON(mmu->hwpagetable == NULL);
76 BUG_ON(mmu->hwpagetable->priv == NULL);
77
78 domain = mmu->hwpagetable->priv;
79
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070080 for (i = 0; i < iommu->dev_count; i++) {
81 iommu_detach_device(domain, iommu->dev[i].dev);
82 iommu->dev[i].attached = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060083 KGSL_MEM_INFO(mmu->device,
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070084 "iommu %p detached from user dev of MMU: %p\n",
85 domain, mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060086 }
87}
88
89static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
90{
91 struct iommu_domain *domain;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060092 struct kgsl_iommu *iommu = mmu->priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -070093 int i, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060094
95 BUG_ON(mmu->hwpagetable == NULL);
96 BUG_ON(mmu->hwpagetable->priv == NULL);
97
98 domain = mmu->hwpagetable->priv;
99
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700100 for (i = 0; i < iommu->dev_count; i++) {
101 if (iommu->dev[i].attached == 0) {
102 ret = iommu_attach_device(domain, iommu->dev[i].dev);
103 if (ret) {
104 KGSL_MEM_ERR(mmu->device,
105 "Failed to attach device, err %d\n",
106 ret);
107 goto done;
108 }
109
110 iommu->dev[i].attached = 1;
111 KGSL_MEM_INFO(mmu->device,
112 "iommu %p detached from user dev of MMU: %p\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600113 domain, mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600114 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600115 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700116
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600117done:
118 return ret;
119}
120
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700121static int _get_iommu_ctxs(struct kgsl_iommu *iommu, struct kgsl_device *device,
122 struct kgsl_device_iommu_data *data)
123{
124 int i;
125
126 for (i = 0; i < data->iommu_ctx_count; i++) {
127 if (iommu->dev_count >= KGSL_IOMMU_MAX_DEV) {
128 KGSL_CORE_ERR("Tried to attach too many IOMMU "
129 "devices\n");
130 return -ENOMEM;
131 }
132
133 if (!data->iommu_ctx_names[i])
134 continue;
135
136 iommu->dev[iommu->dev_count].dev =
137 msm_iommu_get_ctx(data->iommu_ctx_names[i]);
138 if (iommu->dev[iommu->dev_count].dev == NULL) {
139 KGSL_CORE_ERR("Failed to iommu dev handle for "
140 "device %s\n", data->iommu_ctx_names[i]);
141 return -EINVAL;
142 }
143
144 iommu->dev_count++;
145 }
146
147 return 0;
148}
149
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600150static int kgsl_get_iommu_ctxt(struct kgsl_iommu *iommu,
151 struct kgsl_device *device)
152{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600153 struct platform_device *pdev =
154 container_of(device->parentdev, struct platform_device, dev);
155 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700156 int i, ret = 0;
157
158 /* Go through the IOMMU data and attach all the domains */
159
160 for (i = 0; i < pdata_dev->iommu_count; i++) {
161 ret = _get_iommu_ctxs(iommu, device,
162 &pdata_dev->iommu_data[i]);
163 if (ret)
164 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600165 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700166
167 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600168}
169
170static void kgsl_iommu_setstate(struct kgsl_device *device,
171 struct kgsl_pagetable *pagetable)
172{
173 struct kgsl_mmu *mmu = &device->mmu;
174
175 if (mmu->flags & KGSL_FLAGS_STARTED) {
176 /* page table not current, then setup mmu to use new
177 * specified page table
178 */
179 if (mmu->hwpagetable != pagetable) {
180 kgsl_idle(device, KGSL_TIMEOUT_DEFAULT);
181 kgsl_detach_pagetable_iommu_domain(mmu);
182 mmu->hwpagetable = pagetable;
183 if (mmu->hwpagetable)
184 kgsl_attach_pagetable_iommu_domain(mmu);
185 }
186 }
187}
188
189static int kgsl_iommu_init(struct kgsl_device *device)
190{
191 /*
192 * intialize device mmu
193 *
194 * call this with the global lock held
195 */
196 int status = 0;
197 struct kgsl_mmu *mmu = &device->mmu;
198 struct kgsl_iommu *iommu;
199
200 mmu->device = device;
201
202 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
203 if (!iommu) {
204 KGSL_CORE_ERR("kzalloc(%d) failed\n",
205 sizeof(struct kgsl_iommu));
206 return -ENOMEM;
207 }
208
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600209 status = kgsl_get_iommu_ctxt(iommu, device);
210 if (status) {
211 kfree(iommu);
212 iommu = NULL;
213 }
214 mmu->priv = iommu;
215
216 dev_info(device->dev, "|%s| MMU type set for device is IOMMU\n",
217 __func__);
218 return status;
219}
220
221static int kgsl_iommu_start(struct kgsl_device *device)
222{
223 int status;
224 struct kgsl_mmu *mmu = &device->mmu;
225
226 if (mmu->flags & KGSL_FLAGS_STARTED)
227 return 0;
228
229 kgsl_regwrite(device, MH_MMU_CONFIG, 0x00000000);
230 if (mmu->defaultpagetable == NULL)
231 mmu->defaultpagetable =
232 kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
233 /* Return error if the default pagetable doesn't exist */
234 if (mmu->defaultpagetable == NULL)
235 return -ENOMEM;
236 mmu->hwpagetable = mmu->defaultpagetable;
237
238 status = kgsl_attach_pagetable_iommu_domain(mmu);
239 if (!status)
240 mmu->flags |= KGSL_FLAGS_STARTED;
241
242 return status;
243}
244
245static int
246kgsl_iommu_unmap(void *mmu_specific_pt,
247 struct kgsl_memdesc *memdesc)
248{
249 int ret;
250 unsigned int range = memdesc->size;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600251 struct iommu_domain *domain = (struct iommu_domain *)
252 mmu_specific_pt;
253
254 /* All GPU addresses as assigned are page aligned, but some
255 functions purturb the gpuaddr with an offset, so apply the
256 mask here to make sure we have the right address */
257
258 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
259
260 if (range == 0 || gpuaddr == 0)
261 return 0;
262
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600263 ret = iommu_unmap_range(domain, gpuaddr, range);
264 if (ret)
265 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
266 "with err: %d\n", domain, gpuaddr,
267 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600268
269 return 0;
270}
271
272static int
273kgsl_iommu_map(void *mmu_specific_pt,
274 struct kgsl_memdesc *memdesc,
275 unsigned int protflags)
276{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600277 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600278 unsigned int iommu_virt_addr;
Jordan Croused17e9aa2011-10-12 16:57:48 -0600279 struct iommu_domain *domain = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600280
281 BUG_ON(NULL == domain);
282
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600283
Jordan Croused17e9aa2011-10-12 16:57:48 -0600284 iommu_virt_addr = memdesc->gpuaddr;
285
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600286 ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg,
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800287 memdesc->size, 0);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600288 if (ret) {
289 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
290 "failed with err: %d\n", domain,
291 iommu_virt_addr, memdesc->sg, memdesc->size,
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800292 0, ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600293 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600294 }
295
296 return ret;
297}
298
299static int kgsl_iommu_stop(struct kgsl_device *device)
300{
301 /*
302 * stop device mmu
303 *
304 * call this with the global lock held
305 */
306 struct kgsl_mmu *mmu = &device->mmu;
307
308 if (mmu->flags & KGSL_FLAGS_STARTED) {
309 /* detach iommu attachment */
310 kgsl_detach_pagetable_iommu_domain(mmu);
311
312 mmu->flags &= ~KGSL_FLAGS_STARTED;
313 }
314
315 return 0;
316}
317
318static int kgsl_iommu_close(struct kgsl_device *device)
319{
320 struct kgsl_mmu *mmu = &device->mmu;
321 if (mmu->defaultpagetable)
322 kgsl_mmu_putpagetable(mmu->defaultpagetable);
323
324 return 0;
325}
326
327static unsigned int
328kgsl_iommu_get_current_ptbase(struct kgsl_device *device)
329{
330 /* Current base is always the hwpagetables domain as we
331 * do not use per process pagetables right not for iommu.
332 * This will change when we switch to per process pagetables.
333 */
334 return (unsigned int)device->mmu.hwpagetable->priv;
335}
336
337struct kgsl_mmu_ops iommu_ops = {
338 .mmu_init = kgsl_iommu_init,
339 .mmu_close = kgsl_iommu_close,
340 .mmu_start = kgsl_iommu_start,
341 .mmu_stop = kgsl_iommu_stop,
342 .mmu_setstate = kgsl_iommu_setstate,
343 .mmu_device_setstate = NULL,
344 .mmu_pagefault = NULL,
345 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
346};
347
348struct kgsl_mmu_pt_ops iommu_pt_ops = {
349 .mmu_map = kgsl_iommu_map,
350 .mmu_unmap = kgsl_iommu_unmap,
351 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
352 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
353 .mmu_pt_equal = kgsl_iommu_pt_equal,
354 .mmu_pt_get_flags = NULL,
355};