blob: 0a33f7595faf993d926f68fa757ffc84f036f4e2 [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
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -060027/*
28 * kgsl_iommu_disable_clk - Disable iommu clocks
29 * @mmu - Pointer to mmu structure
30 *
31 * Disables iommu clocks
32 * Return - void
33 */
34static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
35{
36 struct kgsl_iommu *iommu = mmu->priv;
37 struct msm_iommu_drvdata *iommu_drvdata;
38 int i, j;
39
40 for (i = 0; i < iommu->unit_count; i++) {
41 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
42 for (j = 0; j < iommu_unit->dev_count; j++) {
43 if (!iommu_unit->dev[j].clk_enabled)
44 continue;
45 iommu_drvdata = dev_get_drvdata(
46 iommu_unit->dev[j].dev->parent);
47 if (iommu_drvdata->clk)
48 clk_disable_unprepare(iommu_drvdata->clk);
49 clk_disable_unprepare(iommu_drvdata->pclk);
50 iommu_unit->dev[j].clk_enabled = false;
51 }
52 }
53}
54
55/*
56 * kgsl_iommu_enable_clk - Enable iommu clocks
57 * @mmu - Pointer to mmu structure
58 * @ctx_id - The context bank whose clocks are to be turned on
59 *
60 * Enables iommu clocks of a given context
61 * Return: 0 on success else error code
62 */
63static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
64 int ctx_id)
65{
66 int ret = 0;
67 int i, j;
68 struct kgsl_iommu *iommu = mmu->priv;
69 struct msm_iommu_drvdata *iommu_drvdata;
70
71 for (i = 0; i < iommu->unit_count; i++) {
72 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
73 for (j = 0; j < iommu_unit->dev_count; j++) {
74 if (iommu_unit->dev[j].clk_enabled ||
75 ctx_id != iommu_unit->dev[j].ctx_id)
76 continue;
77 iommu_drvdata =
78 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
79 ret = clk_prepare_enable(iommu_drvdata->pclk);
80 if (ret)
81 goto done;
82 if (iommu_drvdata->clk) {
83 ret = clk_prepare_enable(iommu_drvdata->clk);
84 if (ret) {
85 clk_disable_unprepare(
86 iommu_drvdata->pclk);
87 goto done;
88 }
89 }
90 iommu_unit->dev[j].clk_enabled = true;
91 }
92 }
93done:
94 if (ret)
95 kgsl_iommu_disable_clk(mmu);
96 return ret;
97}
98
Shubhraprakash Das48d97302012-05-07 12:16:08 -060099/*
100 * kgsl_iommu_pt_equal - Check if pagetables are equal
101 * @pt - Pointer to pagetable
102 * @pt_base - Address of a pagetable that the IOMMU register is
103 * programmed with
104 *
105 * Checks whether the pt_base is equal to the base address of
106 * the pagetable which is contained in the pt structure
107 * Return - Non-zero if the pagetable addresses are equal else 0
108 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600109static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
110 unsigned int pt_base)
111{
Shubhraprakash Das528aa462012-03-01 14:56:28 -0700112 struct iommu_domain *domain = pt ? pt->priv : NULL;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600113 unsigned int domain_ptbase = domain ? iommu_get_pt_base_addr(domain) :
114 0;
115 /* Only compare the valid address bits of the pt_base */
116 domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
117 KGSL_IOMMU_TTBR0_PA_SHIFT);
118 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
119 KGSL_IOMMU_TTBR0_PA_SHIFT);
120 return domain_ptbase && pt_base &&
121 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600122}
123
124static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
125{
126 struct iommu_domain *domain = mmu_specific_pt;
127 if (domain)
128 iommu_domain_free(domain);
129}
130
131void *kgsl_iommu_create_pagetable(void)
132{
133 struct iommu_domain *domain = iommu_domain_alloc(0);
134 if (!domain)
135 KGSL_CORE_ERR("Failed to create iommu domain\n");
136
137 return domain;
138}
139
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600140/*
141 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
142 * pagetable
143 * @mmu - Pointer to the device mmu structure
144 * @priv - Flag indicating whether the private or user context is to be
145 * detached
146 *
147 * Detach the IOMMU unit with the domain that is contained in the
148 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
149 * in use because the PTBR will not be set after a detach
150 * Return - void
151 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600152static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
153{
154 struct iommu_domain *domain;
155 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600156 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600157
158 BUG_ON(mmu->hwpagetable == NULL);
159 BUG_ON(mmu->hwpagetable->priv == NULL);
160
161 domain = mmu->hwpagetable->priv;
162
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600163 for (i = 0; i < iommu->unit_count; i++) {
164 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
165 for (j = 0; j < iommu_unit->dev_count; j++) {
166 if (iommu_unit->dev[j].attached) {
167 iommu_detach_device(domain,
168 iommu_unit->dev[j].dev);
169 iommu_unit->dev[j].attached = false;
170 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
171 "from user dev of MMU: %p\n",
172 domain, mmu);
173 }
174 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600175 }
176}
177
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600178/*
179 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
180 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
181 * setup other IOMMU registers for the device so that it becomes
182 * active
183 * @mmu - Pointer to the device mmu structure
184 * @priv - Flag indicating whether the private or user context is to be
185 * attached
186 *
187 * Attach the IOMMU unit with the domain that is contained in the
188 * hwpagetable of the given mmu.
189 * Return - 0 on success else error code
190 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600191static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
192{
193 struct iommu_domain *domain;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600194 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600195 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600196
197 BUG_ON(mmu->hwpagetable == NULL);
198 BUG_ON(mmu->hwpagetable->priv == NULL);
199
200 domain = mmu->hwpagetable->priv;
201
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600202 /*
203 * Loop through all the iommu devcies under all iommu units and
204 * attach the domain
205 */
206 for (i = 0; i < iommu->unit_count; i++) {
207 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
208 for (j = 0; j < iommu_unit->dev_count; j++) {
209 if (!iommu_unit->dev[j].attached) {
210 ret = iommu_attach_device(domain,
211 iommu_unit->dev[j].dev);
212 if (ret) {
213 KGSL_MEM_ERR(mmu->device,
214 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700215 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600216 goto done;
217 }
218 iommu_unit->dev[j].attached = true;
219 KGSL_MEM_INFO(mmu->device,
220 "iommu pt %p attached to dev %p, ctx_id %d\n",
221 domain, iommu_unit->dev[j].dev,
222 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700223 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600224 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600225 }
226done:
227 return ret;
228}
229
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600230/*
231 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
232 * @mmu - Pointer to mmu device
233 * data - Pointer to the platform data containing information about
234 * iommu devices for one iommu unit
235 * unit_id - The IOMMU unit number. This is not a specific ID but just
236 * a serial number. The serial numbers are treated as ID's of the
237 * IOMMU units
238 *
239 * Return - 0 on success else error code
240 */
241static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
242 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700243{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600244 struct kgsl_iommu *iommu = mmu->priv;
245 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700246 int i;
247
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600248 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
249 KGSL_CORE_ERR("Too many iommu devices defined for an "
250 "IOMMU unit\n");
251 return -EINVAL;
252 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700253
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600254 for (i = 0; i < data->iommu_ctx_count; i++) {
255 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700256 continue;
257
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600258 iommu_unit->dev[iommu_unit->dev_count].dev =
259 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
260 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
261 KGSL_CORE_ERR("Failed to get iommu dev handle for "
262 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700263 return -EINVAL;
264 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600265 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
266 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
267 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
268 data->iommu_ctxs[i].ctx_id);
269 return -EINVAL;
270 }
271 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
272 data->iommu_ctxs[i].ctx_id;
273 KGSL_DRV_INFO(mmu->device,
274 "Obtained dev handle %p for iommu context %s\n",
275 iommu_unit->dev[iommu_unit->dev_count].dev,
276 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700277
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600278 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700279 }
280
281 return 0;
282}
283
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600284/*
285 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
286 * @mmu - Pointer to mmu device
287 *
288 * Get the device pointers for the IOMMU user and priv contexts of the
289 * kgsl device
290 * Return - 0 on success else error code
291 */
292static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600293{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600294 struct platform_device *pdev =
295 container_of(mmu->device->parentdev, struct platform_device,
296 dev);
297 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
298 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700299 int i, ret = 0;
300
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600301 /* Go through the IOMMU data and get all the context devices */
302 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
303 KGSL_CORE_ERR("Too many IOMMU units defined\n");
304 ret = -EINVAL;
305 goto done;
306 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700307
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600308 for (i = 0; i < pdata_dev->iommu_count; i++) {
309 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700310 if (ret)
311 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600312 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600313 iommu->unit_count = pdata_dev->iommu_count;
314done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700315 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600316}
317
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600318/*
319 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
320 * of the respective iommu units
321 * @mmu - Pointer to mmu structure
322 *
323 * Return - 0 on success else error code
324 */
325static int kgsl_set_register_map(struct kgsl_mmu *mmu)
326{
327 struct platform_device *pdev =
328 container_of(mmu->device->parentdev, struct platform_device,
329 dev);
330 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
331 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
332 struct kgsl_iommu_unit *iommu_unit;
333 int i = 0, ret = 0;
334
335 for (; i < pdata_dev->iommu_count; i++) {
336 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
337 iommu_unit = &iommu->iommu_units[i];
338 /* set up the IOMMU register map for the given IOMMU unit */
339 if (!data.physstart || !data.physend) {
340 KGSL_CORE_ERR("The register range for IOMMU unit not"
341 " specified\n");
342 ret = -EINVAL;
343 goto err;
344 }
345 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
346 data.physend - data.physstart + 1);
347 if (!iommu_unit->reg_map.hostptr) {
348 KGSL_CORE_ERR("Failed to map SMMU register address "
349 "space from %x to %x\n", data.physstart,
350 data.physend - data.physstart + 1);
351 ret = -ENOMEM;
352 i--;
353 goto err;
354 }
355 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
356 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600357 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
358 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600359 }
360 iommu->unit_count = pdata_dev->iommu_count;
361 return ret;
362err:
363 /* Unmap any mapped IOMMU regions */
364 for (; i >= 0; i--) {
365 iommu_unit = &iommu->iommu_units[i];
366 iounmap(iommu_unit->reg_map.hostptr);
367 iommu_unit->reg_map.size = 0;
368 iommu_unit->reg_map.physaddr = 0;
369 }
370 return ret;
371}
372
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600373static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600374 struct kgsl_pagetable *pagetable)
375{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600376 if (mmu->flags & KGSL_FLAGS_STARTED) {
377 /* page table not current, then setup mmu to use new
378 * specified page table
379 */
380 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600381 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600382 kgsl_detach_pagetable_iommu_domain(mmu);
383 mmu->hwpagetable = pagetable;
384 if (mmu->hwpagetable)
385 kgsl_attach_pagetable_iommu_domain(mmu);
386 }
387 }
388}
389
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600390static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600391{
392 /*
393 * intialize device mmu
394 *
395 * call this with the global lock held
396 */
397 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600398 struct kgsl_iommu *iommu;
399
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600400 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
401 if (!iommu) {
402 KGSL_CORE_ERR("kzalloc(%d) failed\n",
403 sizeof(struct kgsl_iommu));
404 return -ENOMEM;
405 }
406
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600407 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600408 status = kgsl_get_iommu_ctxt(mmu);
409 if (status)
410 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600411 status = kgsl_set_register_map(mmu);
412 if (status)
413 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600414
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600415 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600416 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600417done:
418 if (status) {
419 kfree(iommu);
420 mmu->priv = NULL;
421 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600422 return status;
423}
424
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600425/*
426 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
427 * for iommu. This function is only called once during first start, successive
428 * start do not call this funciton.
429 * @mmu - Pointer to mmu structure
430 *
431 * Create the initial defaultpagetable and setup the iommu mappings to it
432 * Return - 0 on success else error code
433 */
434static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
435{
436 int status = 0;
437 int i = 0;
438 struct kgsl_iommu *iommu = mmu->priv;
439
440 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
441 /* Return error if the default pagetable doesn't exist */
442 if (mmu->defaultpagetable == NULL) {
443 status = -ENOMEM;
444 goto err;
445 }
446 /* Map the IOMMU regsiters to only defaultpagetable */
447 for (i = 0; i < iommu->unit_count; i++) {
448 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
449 status = kgsl_mmu_map(mmu->defaultpagetable,
450 &(iommu->iommu_units[i].reg_map),
451 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
452 if (status) {
453 iommu->iommu_units[i].reg_map.priv &=
454 ~KGSL_MEMFLAGS_GLOBAL;
455 goto err;
456 }
457 }
458 return status;
459err:
460 for (i--; i >= 0; i--) {
461 kgsl_mmu_unmap(mmu->defaultpagetable,
462 &(iommu->iommu_units[i].reg_map));
463 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
464 }
465 if (mmu->defaultpagetable) {
466 kgsl_mmu_putpagetable(mmu->defaultpagetable);
467 mmu->defaultpagetable = NULL;
468 }
469 return status;
470}
471
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600472static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600473{
474 int status;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600475
476 if (mmu->flags & KGSL_FLAGS_STARTED)
477 return 0;
478
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600479 if (mmu->defaultpagetable == NULL) {
480 status = kgsl_iommu_setup_defaultpagetable(mmu);
481 if (status)
482 return -ENOMEM;
483 }
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600484 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600485
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600486 mmu->hwpagetable = mmu->defaultpagetable;
487
488 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600489 if (!status) {
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600490 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600491 } else {
492 kgsl_detach_pagetable_iommu_domain(mmu);
493 mmu->hwpagetable = NULL;
494 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600495
496 return status;
497}
498
499static int
500kgsl_iommu_unmap(void *mmu_specific_pt,
501 struct kgsl_memdesc *memdesc)
502{
503 int ret;
504 unsigned int range = memdesc->size;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600505 struct iommu_domain *domain = (struct iommu_domain *)
506 mmu_specific_pt;
507
508 /* All GPU addresses as assigned are page aligned, but some
509 functions purturb the gpuaddr with an offset, so apply the
510 mask here to make sure we have the right address */
511
512 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
513
514 if (range == 0 || gpuaddr == 0)
515 return 0;
516
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600517 ret = iommu_unmap_range(domain, gpuaddr, range);
518 if (ret)
519 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
520 "with err: %d\n", domain, gpuaddr,
521 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600522
523 return 0;
524}
525
526static int
527kgsl_iommu_map(void *mmu_specific_pt,
528 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600529 unsigned int protflags,
530 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600531{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600532 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600533 unsigned int iommu_virt_addr;
Jordan Croused17e9aa2011-10-12 16:57:48 -0600534 struct iommu_domain *domain = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600535
536 BUG_ON(NULL == domain);
537
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600538
Jordan Croused17e9aa2011-10-12 16:57:48 -0600539 iommu_virt_addr = memdesc->gpuaddr;
540
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600541 ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg,
Olav Hauganf310cf22012-05-08 08:42:49 -0700542 memdesc->size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600543 if (ret) {
544 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
545 "failed with err: %d\n", domain,
546 iommu_virt_addr, memdesc->sg, memdesc->size,
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800547 0, ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600548 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600549 }
550
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600551#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
552 /*
553 * Flushing only required if per process pagetables are used. With
554 * global case, flushing will happen inside iommu_map function
555 */
556 if (!ret)
557 *tlb_flags = UINT_MAX;
558#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600559 return ret;
560}
561
Shubhraprakash Das79447952012-04-26 18:12:23 -0600562static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600563{
564 /*
565 * stop device mmu
566 *
567 * call this with the global lock held
568 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600569
570 if (mmu->flags & KGSL_FLAGS_STARTED) {
571 /* detach iommu attachment */
572 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600573 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600574
575 mmu->flags &= ~KGSL_FLAGS_STARTED;
576 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600577}
578
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600579static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600580{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600581 struct kgsl_iommu *iommu = mmu->priv;
582 int i;
583 for (i = 0; i < iommu->unit_count; i++) {
584 if (iommu->iommu_units[i].reg_map.gpuaddr)
585 kgsl_mmu_unmap(mmu->defaultpagetable,
586 &(iommu->iommu_units[i].reg_map));
587 if (iommu->iommu_units[i].reg_map.hostptr)
588 iounmap(iommu->iommu_units[i].reg_map.hostptr);
589 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
590 iommu->iommu_units[i].reg_map.sglen);
591 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600592 if (mmu->defaultpagetable)
593 kgsl_mmu_putpagetable(mmu->defaultpagetable);
594
595 return 0;
596}
597
598static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600599kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600600{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600601 unsigned int pt_base;
602 struct kgsl_iommu *iommu = mmu->priv;
603 /* Return the current pt base by reading IOMMU pt_base register */
604 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
605 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
606 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
607 KGSL_IOMMU_TTBR0);
608 kgsl_iommu_disable_clk(mmu);
609 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
610 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600611}
612
613struct kgsl_mmu_ops iommu_ops = {
614 .mmu_init = kgsl_iommu_init,
615 .mmu_close = kgsl_iommu_close,
616 .mmu_start = kgsl_iommu_start,
617 .mmu_stop = kgsl_iommu_stop,
618 .mmu_setstate = kgsl_iommu_setstate,
619 .mmu_device_setstate = NULL,
620 .mmu_pagefault = NULL,
621 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600622 .mmu_enable_clk = kgsl_iommu_enable_clk,
623 .mmu_disable_clk = kgsl_iommu_disable_clk,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600624};
625
626struct kgsl_mmu_pt_ops iommu_pt_ops = {
627 .mmu_map = kgsl_iommu_map,
628 .mmu_unmap = kgsl_iommu_unmap,
629 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
630 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
631 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600632};