blob: dd32ee4f72ab88f32401f9adf1fdfba9e598bbb0 [file] [log] [blame]
Shubhraprakash Das79447952012-04-26 18:12:23 -06001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06002 *
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>
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060019#include <linux/msm_kgsl.h>
20
21#include "kgsl.h"
22#include "kgsl_device.h"
23#include "kgsl_mmu.h"
24#include "kgsl_sharedmem.h"
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060025#include "kgsl_iommu.h"
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060026
27static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
28 unsigned int pt_base)
29{
Shubhraprakash Das528aa462012-03-01 14:56:28 -070030 struct iommu_domain *domain = pt ? pt->priv : NULL;
31 return domain && pt_base && ((unsigned int)domain == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060032}
33
34static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
35{
36 struct iommu_domain *domain = mmu_specific_pt;
37 if (domain)
38 iommu_domain_free(domain);
39}
40
41void *kgsl_iommu_create_pagetable(void)
42{
43 struct iommu_domain *domain = iommu_domain_alloc(0);
44 if (!domain)
45 KGSL_CORE_ERR("Failed to create iommu domain\n");
46
47 return domain;
48}
49
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060050/*
51 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
52 * pagetable
53 * @mmu - Pointer to the device mmu structure
54 * @priv - Flag indicating whether the private or user context is to be
55 * detached
56 *
57 * Detach the IOMMU unit with the domain that is contained in the
58 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
59 * in use because the PTBR will not be set after a detach
60 * Return - void
61 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060062static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
63{
64 struct iommu_domain *domain;
65 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060066 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060067
68 BUG_ON(mmu->hwpagetable == NULL);
69 BUG_ON(mmu->hwpagetable->priv == NULL);
70
71 domain = mmu->hwpagetable->priv;
72
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060073 for (i = 0; i < iommu->unit_count; i++) {
74 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
75 for (j = 0; j < iommu_unit->dev_count; j++) {
76 if (iommu_unit->dev[j].attached) {
77 iommu_detach_device(domain,
78 iommu_unit->dev[j].dev);
79 iommu_unit->dev[j].attached = false;
80 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
81 "from user dev of MMU: %p\n",
82 domain, mmu);
83 }
84 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060085 }
86}
87
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060088/*
89 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
90 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
91 * setup other IOMMU registers for the device so that it becomes
92 * active
93 * @mmu - Pointer to the device mmu structure
94 * @priv - Flag indicating whether the private or user context is to be
95 * attached
96 *
97 * Attach the IOMMU unit with the domain that is contained in the
98 * hwpagetable of the given mmu.
99 * Return - 0 on success else error code
100 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600101static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
102{
103 struct iommu_domain *domain;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600104 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600105 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600106
107 BUG_ON(mmu->hwpagetable == NULL);
108 BUG_ON(mmu->hwpagetable->priv == NULL);
109
110 domain = mmu->hwpagetable->priv;
111
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600112 /*
113 * Loop through all the iommu devcies under all iommu units and
114 * attach the domain
115 */
116 for (i = 0; i < iommu->unit_count; i++) {
117 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
118 for (j = 0; j < iommu_unit->dev_count; j++) {
119 if (!iommu_unit->dev[j].attached) {
120 ret = iommu_attach_device(domain,
121 iommu_unit->dev[j].dev);
122 if (ret) {
123 KGSL_MEM_ERR(mmu->device,
124 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700125 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600126 goto done;
127 }
128 iommu_unit->dev[j].attached = true;
129 KGSL_MEM_INFO(mmu->device,
130 "iommu pt %p attached to dev %p, ctx_id %d\n",
131 domain, iommu_unit->dev[j].dev,
132 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700133 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600134 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600135 }
136done:
137 return ret;
138}
139
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600140/*
141 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
142 * @mmu - Pointer to mmu device
143 * data - Pointer to the platform data containing information about
144 * iommu devices for one iommu unit
145 * unit_id - The IOMMU unit number. This is not a specific ID but just
146 * a serial number. The serial numbers are treated as ID's of the
147 * IOMMU units
148 *
149 * Return - 0 on success else error code
150 */
151static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
152 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700153{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600154 struct kgsl_iommu *iommu = mmu->priv;
155 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700156 int i;
157
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600158 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
159 KGSL_CORE_ERR("Too many iommu devices defined for an "
160 "IOMMU unit\n");
161 return -EINVAL;
162 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700163
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600164 for (i = 0; i < data->iommu_ctx_count; i++) {
165 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700166 continue;
167
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600168 iommu_unit->dev[iommu_unit->dev_count].dev =
169 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
170 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
171 KGSL_CORE_ERR("Failed to get iommu dev handle for "
172 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700173 return -EINVAL;
174 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600175 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
176 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
177 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
178 data->iommu_ctxs[i].ctx_id);
179 return -EINVAL;
180 }
181 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
182 data->iommu_ctxs[i].ctx_id;
183 KGSL_DRV_INFO(mmu->device,
184 "Obtained dev handle %p for iommu context %s\n",
185 iommu_unit->dev[iommu_unit->dev_count].dev,
186 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700187
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600188 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700189 }
190
191 return 0;
192}
193
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600194/*
195 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
196 * @mmu - Pointer to mmu device
197 *
198 * Get the device pointers for the IOMMU user and priv contexts of the
199 * kgsl device
200 * Return - 0 on success else error code
201 */
202static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600203{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600204 struct platform_device *pdev =
205 container_of(mmu->device->parentdev, struct platform_device,
206 dev);
207 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
208 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700209 int i, ret = 0;
210
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600211 /* Go through the IOMMU data and get all the context devices */
212 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
213 KGSL_CORE_ERR("Too many IOMMU units defined\n");
214 ret = -EINVAL;
215 goto done;
216 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700217
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600218 for (i = 0; i < pdata_dev->iommu_count; i++) {
219 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700220 if (ret)
221 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600222 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600223 iommu->unit_count = pdata_dev->iommu_count;
224done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700225 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600226}
227
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600228static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600229 struct kgsl_pagetable *pagetable)
230{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600231 if (mmu->flags & KGSL_FLAGS_STARTED) {
232 /* page table not current, then setup mmu to use new
233 * specified page table
234 */
235 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600236 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600237 kgsl_detach_pagetable_iommu_domain(mmu);
238 mmu->hwpagetable = pagetable;
239 if (mmu->hwpagetable)
240 kgsl_attach_pagetable_iommu_domain(mmu);
241 }
242 }
243}
244
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600245static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600246{
247 /*
248 * intialize device mmu
249 *
250 * call this with the global lock held
251 */
252 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600253 struct kgsl_iommu *iommu;
254
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600255 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
256 if (!iommu) {
257 KGSL_CORE_ERR("kzalloc(%d) failed\n",
258 sizeof(struct kgsl_iommu));
259 return -ENOMEM;
260 }
261
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600262 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600263 status = kgsl_get_iommu_ctxt(mmu);
264 if (status)
265 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600266
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600267 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600268 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600269done:
270 if (status) {
271 kfree(iommu);
272 mmu->priv = NULL;
273 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600274 return status;
275}
276
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600277static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600278{
279 int status;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600280
281 if (mmu->flags & KGSL_FLAGS_STARTED)
282 return 0;
283
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600284 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600285 if (mmu->defaultpagetable == NULL)
286 mmu->defaultpagetable =
287 kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
288 /* Return error if the default pagetable doesn't exist */
289 if (mmu->defaultpagetable == NULL)
290 return -ENOMEM;
291 mmu->hwpagetable = mmu->defaultpagetable;
292
293 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600294 if (!status) {
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600295 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600296 } else {
297 kgsl_detach_pagetable_iommu_domain(mmu);
298 mmu->hwpagetable = NULL;
299 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600300
301 return status;
302}
303
304static int
305kgsl_iommu_unmap(void *mmu_specific_pt,
306 struct kgsl_memdesc *memdesc)
307{
308 int ret;
309 unsigned int range = memdesc->size;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600310 struct iommu_domain *domain = (struct iommu_domain *)
311 mmu_specific_pt;
312
313 /* All GPU addresses as assigned are page aligned, but some
314 functions purturb the gpuaddr with an offset, so apply the
315 mask here to make sure we have the right address */
316
317 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
318
319 if (range == 0 || gpuaddr == 0)
320 return 0;
321
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600322 ret = iommu_unmap_range(domain, gpuaddr, range);
323 if (ret)
324 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
325 "with err: %d\n", domain, gpuaddr,
326 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600327
328 return 0;
329}
330
331static int
332kgsl_iommu_map(void *mmu_specific_pt,
333 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600334 unsigned int protflags,
335 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600336{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600337 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600338 unsigned int iommu_virt_addr;
Jordan Croused17e9aa2011-10-12 16:57:48 -0600339 struct iommu_domain *domain = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600340
341 BUG_ON(NULL == domain);
342
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600343
Jordan Croused17e9aa2011-10-12 16:57:48 -0600344 iommu_virt_addr = memdesc->gpuaddr;
345
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600346 ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg,
Olav Hauganf310cf22012-05-08 08:42:49 -0700347 memdesc->size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600348 if (ret) {
349 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
350 "failed with err: %d\n", domain,
351 iommu_virt_addr, memdesc->sg, memdesc->size,
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800352 0, ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600353 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600354 }
355
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600356#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
357 /*
358 * Flushing only required if per process pagetables are used. With
359 * global case, flushing will happen inside iommu_map function
360 */
361 if (!ret)
362 *tlb_flags = UINT_MAX;
363#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600364 return ret;
365}
366
Shubhraprakash Das79447952012-04-26 18:12:23 -0600367static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600368{
369 /*
370 * stop device mmu
371 *
372 * call this with the global lock held
373 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600374
375 if (mmu->flags & KGSL_FLAGS_STARTED) {
376 /* detach iommu attachment */
377 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600378 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600379
380 mmu->flags &= ~KGSL_FLAGS_STARTED;
381 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600382}
383
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600384static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600385{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600386 if (mmu->defaultpagetable)
387 kgsl_mmu_putpagetable(mmu->defaultpagetable);
388
389 return 0;
390}
391
392static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600393kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600394{
395 /* Current base is always the hwpagetables domain as we
396 * do not use per process pagetables right not for iommu.
397 * This will change when we switch to per process pagetables.
398 */
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600399 return (unsigned int)mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600400}
401
402struct kgsl_mmu_ops iommu_ops = {
403 .mmu_init = kgsl_iommu_init,
404 .mmu_close = kgsl_iommu_close,
405 .mmu_start = kgsl_iommu_start,
406 .mmu_stop = kgsl_iommu_stop,
407 .mmu_setstate = kgsl_iommu_setstate,
408 .mmu_device_setstate = NULL,
409 .mmu_pagefault = NULL,
410 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
411};
412
413struct kgsl_mmu_pt_ops iommu_pt_ops = {
414 .mmu_map = kgsl_iommu_map,
415 .mmu_unmap = kgsl_iommu_unmap,
416 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
417 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
418 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600419};