blob: 20508278bb0e3c13e5dfd1e5b636b8e34ec19937 [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 Dase9541a32012-05-09 22:25:55 -060026#include "adreno_pm4types.h"
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -060027#include "adreno.h"
Jordan Crouse95b68472012-05-25 10:25:01 -060028#include "kgsl_trace.h"
29
30static struct kgsl_iommu_unit *get_iommu_unit(struct device *dev)
31{
32 int i, j, k;
33
34 for (i = 0; i < KGSL_DEVICE_MAX; i++) {
35 struct kgsl_mmu *mmu;
36 struct kgsl_iommu *iommu;
37
38 if (kgsl_driver.devp[i] == NULL)
39 continue;
40
41 mmu = kgsl_get_mmu(kgsl_driver.devp[i]);
42 if (mmu == NULL || mmu->priv == NULL)
43 continue;
44
45 iommu = mmu->priv;
46
47 for (j = 0; j < iommu->unit_count; j++) {
48 struct kgsl_iommu_unit *iommu_unit =
49 &iommu->iommu_units[j];
50 for (k = 0; k < iommu_unit->dev_count; k++) {
51 if (iommu_unit->dev[k].dev == dev)
52 return iommu_unit;
53 }
54 }
55 }
56
57 return NULL;
58}
59
60static struct kgsl_iommu_device *get_iommu_device(struct kgsl_iommu_unit *unit,
61 struct device *dev)
62{
63 int k;
64
65 for (k = 0; unit && k < unit->dev_count; k++) {
66 if (unit->dev[k].dev == dev)
67 return &(unit->dev[k]);
68 }
69
70 return NULL;
71}
72
73static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
74 struct device *dev, unsigned long addr, int flags)
75{
76 struct kgsl_iommu_unit *iommu_unit = get_iommu_unit(dev);
77 struct kgsl_iommu_device *iommu_dev = get_iommu_device(iommu_unit, dev);
78 unsigned int ptbase, fsr;
79
80 if (!iommu_dev) {
81 KGSL_CORE_ERR("Invalid IOMMU device %p\n", dev);
82 return -ENOSYS;
83 }
84
85 ptbase = iommu_get_pt_base_addr(domain);
86
87 fsr = KGSL_IOMMU_GET_IOMMU_REG(iommu_unit->reg_map.hostptr,
88 iommu_dev->ctx_id, FSR);
89
90 KGSL_MEM_CRIT(iommu_dev->kgsldev,
91 "GPU PAGE FAULT: addr = %lX pid = %d\n",
92 addr, kgsl_mmu_get_ptname_from_ptbase(ptbase));
93 KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
94 iommu_dev->ctx_id, fsr);
95
96 trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
97 kgsl_mmu_get_ptname_from_ptbase(ptbase), 0);
98
99 return 0;
100}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600101
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600102/*
103 * kgsl_iommu_disable_clk - Disable iommu clocks
104 * @mmu - Pointer to mmu structure
105 *
106 * Disables iommu clocks
107 * Return - void
108 */
109static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
110{
111 struct kgsl_iommu *iommu = mmu->priv;
112 struct msm_iommu_drvdata *iommu_drvdata;
113 int i, j;
114
115 for (i = 0; i < iommu->unit_count; i++) {
116 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
117 for (j = 0; j < iommu_unit->dev_count; j++) {
118 if (!iommu_unit->dev[j].clk_enabled)
119 continue;
120 iommu_drvdata = dev_get_drvdata(
121 iommu_unit->dev[j].dev->parent);
122 if (iommu_drvdata->clk)
123 clk_disable_unprepare(iommu_drvdata->clk);
124 clk_disable_unprepare(iommu_drvdata->pclk);
125 iommu_unit->dev[j].clk_enabled = false;
126 }
127 }
128}
129
130/*
131 * kgsl_iommu_enable_clk - Enable iommu clocks
132 * @mmu - Pointer to mmu structure
133 * @ctx_id - The context bank whose clocks are to be turned on
134 *
135 * Enables iommu clocks of a given context
136 * Return: 0 on success else error code
137 */
138static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
139 int ctx_id)
140{
141 int ret = 0;
142 int i, j;
143 struct kgsl_iommu *iommu = mmu->priv;
144 struct msm_iommu_drvdata *iommu_drvdata;
145
146 for (i = 0; i < iommu->unit_count; i++) {
147 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
148 for (j = 0; j < iommu_unit->dev_count; j++) {
149 if (iommu_unit->dev[j].clk_enabled ||
150 ctx_id != iommu_unit->dev[j].ctx_id)
151 continue;
152 iommu_drvdata =
153 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
154 ret = clk_prepare_enable(iommu_drvdata->pclk);
155 if (ret)
156 goto done;
157 if (iommu_drvdata->clk) {
158 ret = clk_prepare_enable(iommu_drvdata->clk);
159 if (ret) {
160 clk_disable_unprepare(
161 iommu_drvdata->pclk);
162 goto done;
163 }
164 }
165 iommu_unit->dev[j].clk_enabled = true;
166 }
167 }
168done:
169 if (ret)
170 kgsl_iommu_disable_clk(mmu);
171 return ret;
172}
173
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600174/*
175 * kgsl_iommu_pt_equal - Check if pagetables are equal
176 * @pt - Pointer to pagetable
177 * @pt_base - Address of a pagetable that the IOMMU register is
178 * programmed with
179 *
180 * Checks whether the pt_base is equal to the base address of
181 * the pagetable which is contained in the pt structure
182 * Return - Non-zero if the pagetable addresses are equal else 0
183 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600184static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
185 unsigned int pt_base)
186{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600187 struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
188 unsigned int domain_ptbase = iommu_pt ?
189 iommu_get_pt_base_addr(iommu_pt->domain) : 0;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600190 /* Only compare the valid address bits of the pt_base */
191 domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
192 KGSL_IOMMU_TTBR0_PA_SHIFT);
193 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
194 KGSL_IOMMU_TTBR0_PA_SHIFT);
195 return domain_ptbase && pt_base &&
196 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600197}
198
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600199/*
200 * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
201 * @mmu_specific_pt - Pointer to pagetable which is to be freed
202 *
203 * Return - void
204 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600205static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
206{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600207 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
208 if (iommu_pt->domain)
209 iommu_domain_free(iommu_pt->domain);
210 if (iommu_pt->iommu) {
211 if ((KGSL_IOMMU_ASID_REUSE == iommu_pt->asid) &&
212 iommu_pt->iommu->asid_reuse)
213 iommu_pt->iommu->asid_reuse--;
214 if (!iommu_pt->iommu->asid_reuse ||
215 (KGSL_IOMMU_ASID_REUSE != iommu_pt->asid))
216 clear_bit(iommu_pt->asid, iommu_pt->iommu->asids);
217 }
218 kfree(iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600219}
220
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600221/*
222 * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
223 *
224 * Allocate memory to hold a pagetable and allocate the IOMMU
225 * domain which is the actual IOMMU pagetable
226 * Return - void
227 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600228void *kgsl_iommu_create_pagetable(void)
229{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600230 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600231
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600232 iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
233 if (!iommu_pt) {
234 KGSL_CORE_ERR("kzalloc(%d) failed\n",
235 sizeof(struct kgsl_iommu_pt));
236 return NULL;
237 }
Jordan Crouse523800f2012-04-16 14:14:45 -0600238 iommu_pt->domain = iommu_domain_alloc(MSM_IOMMU_DOMAIN_PT_CACHEABLE);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600239 if (!iommu_pt->domain) {
240 KGSL_CORE_ERR("Failed to create iommu domain\n");
241 kfree(iommu_pt);
242 return NULL;
Jordan Crouse95b68472012-05-25 10:25:01 -0600243 } else {
244 iommu_set_fault_handler(iommu_pt->domain,
245 kgsl_iommu_fault_handler);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600246 }
Jordan Crouse95b68472012-05-25 10:25:01 -0600247
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600248 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600249}
250
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600251/*
252 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
253 * pagetable
254 * @mmu - Pointer to the device mmu structure
255 * @priv - Flag indicating whether the private or user context is to be
256 * detached
257 *
258 * Detach the IOMMU unit with the domain that is contained in the
259 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
260 * in use because the PTBR will not be set after a detach
261 * Return - void
262 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600263static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
264{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600265 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600266 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600267 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600268
269 BUG_ON(mmu->hwpagetable == NULL);
270 BUG_ON(mmu->hwpagetable->priv == NULL);
271
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600272 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600273
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600274 for (i = 0; i < iommu->unit_count; i++) {
275 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
276 for (j = 0; j < iommu_unit->dev_count; j++) {
277 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600278 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600279 iommu_unit->dev[j].dev);
280 iommu_unit->dev[j].attached = false;
281 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
282 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600283 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600284 }
285 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600286 }
287}
288
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600289/*
290 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
291 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
292 * setup other IOMMU registers for the device so that it becomes
293 * active
294 * @mmu - Pointer to the device mmu structure
295 * @priv - Flag indicating whether the private or user context is to be
296 * attached
297 *
298 * Attach the IOMMU unit with the domain that is contained in the
299 * hwpagetable of the given mmu.
300 * Return - 0 on success else error code
301 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600302static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
303{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600304 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600305 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600306 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600307
308 BUG_ON(mmu->hwpagetable == NULL);
309 BUG_ON(mmu->hwpagetable->priv == NULL);
310
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600311 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600312
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600313 /*
314 * Loop through all the iommu devcies under all iommu units and
315 * attach the domain
316 */
317 for (i = 0; i < iommu->unit_count; i++) {
318 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
319 for (j = 0; j < iommu_unit->dev_count; j++) {
320 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600321 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600322 iommu_unit->dev[j].dev);
323 if (ret) {
324 KGSL_MEM_ERR(mmu->device,
325 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700326 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600327 goto done;
328 }
329 iommu_unit->dev[j].attached = true;
330 KGSL_MEM_INFO(mmu->device,
331 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600332 iommu_pt->domain, iommu_unit->dev[j].dev,
333 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700334 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600335 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600336 }
337done:
338 return ret;
339}
340
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600341/*
342 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
343 * @mmu - Pointer to mmu device
344 * data - Pointer to the platform data containing information about
345 * iommu devices for one iommu unit
346 * unit_id - The IOMMU unit number. This is not a specific ID but just
347 * a serial number. The serial numbers are treated as ID's of the
348 * IOMMU units
349 *
350 * Return - 0 on success else error code
351 */
352static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
353 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700354{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600355 struct kgsl_iommu *iommu = mmu->priv;
356 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700357 int i;
358
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600359 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
360 KGSL_CORE_ERR("Too many iommu devices defined for an "
361 "IOMMU unit\n");
362 return -EINVAL;
363 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700364
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600365 for (i = 0; i < data->iommu_ctx_count; i++) {
366 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700367 continue;
368
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600369 iommu_unit->dev[iommu_unit->dev_count].dev =
370 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
371 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
372 KGSL_CORE_ERR("Failed to get iommu dev handle for "
373 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700374 return -EINVAL;
375 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600376 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
377 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
378 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
379 data->iommu_ctxs[i].ctx_id);
380 return -EINVAL;
381 }
382 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
383 data->iommu_ctxs[i].ctx_id;
Jordan Crouse95b68472012-05-25 10:25:01 -0600384 iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
385
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600386 KGSL_DRV_INFO(mmu->device,
387 "Obtained dev handle %p for iommu context %s\n",
388 iommu_unit->dev[iommu_unit->dev_count].dev,
389 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700390
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600391 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700392 }
393
394 return 0;
395}
396
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600397/*
398 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
399 * @mmu - Pointer to mmu device
400 *
401 * Get the device pointers for the IOMMU user and priv contexts of the
402 * kgsl device
403 * Return - 0 on success else error code
404 */
405static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600406{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600407 struct platform_device *pdev =
408 container_of(mmu->device->parentdev, struct platform_device,
409 dev);
410 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
411 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700412 int i, ret = 0;
413
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600414 /* Go through the IOMMU data and get all the context devices */
415 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
416 KGSL_CORE_ERR("Too many IOMMU units defined\n");
417 ret = -EINVAL;
418 goto done;
419 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700420
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600421 for (i = 0; i < pdata_dev->iommu_count; i++) {
422 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700423 if (ret)
424 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600425 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600426 iommu->unit_count = pdata_dev->iommu_count;
427done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700428 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600429}
430
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600431/*
432 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
433 * of the respective iommu units
434 * @mmu - Pointer to mmu structure
435 *
436 * Return - 0 on success else error code
437 */
438static int kgsl_set_register_map(struct kgsl_mmu *mmu)
439{
440 struct platform_device *pdev =
441 container_of(mmu->device->parentdev, struct platform_device,
442 dev);
443 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
444 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
445 struct kgsl_iommu_unit *iommu_unit;
446 int i = 0, ret = 0;
447
448 for (; i < pdata_dev->iommu_count; i++) {
449 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
450 iommu_unit = &iommu->iommu_units[i];
451 /* set up the IOMMU register map for the given IOMMU unit */
452 if (!data.physstart || !data.physend) {
453 KGSL_CORE_ERR("The register range for IOMMU unit not"
454 " specified\n");
455 ret = -EINVAL;
456 goto err;
457 }
458 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
459 data.physend - data.physstart + 1);
460 if (!iommu_unit->reg_map.hostptr) {
461 KGSL_CORE_ERR("Failed to map SMMU register address "
462 "space from %x to %x\n", data.physstart,
463 data.physend - data.physstart + 1);
464 ret = -ENOMEM;
465 i--;
466 goto err;
467 }
468 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
469 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600470 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
471 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600472 }
473 iommu->unit_count = pdata_dev->iommu_count;
474 return ret;
475err:
476 /* Unmap any mapped IOMMU regions */
477 for (; i >= 0; i--) {
478 iommu_unit = &iommu->iommu_units[i];
479 iounmap(iommu_unit->reg_map.hostptr);
480 iommu_unit->reg_map.size = 0;
481 iommu_unit->reg_map.physaddr = 0;
482 }
483 return ret;
484}
485
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600486/*
487 * kgsl_iommu_pt_get_base_addr - Get the address of the pagetable that the
488 * IOMMU ttbr0 register is programmed with
489 * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
490 *
491 * Return - actual pagetable address that the ttbr0 register is programmed
492 * with
493 */
494static unsigned int kgsl_iommu_pt_get_base_addr(struct kgsl_pagetable *pt)
495{
496 struct kgsl_iommu_pt *iommu_pt = pt->priv;
497 return iommu_get_pt_base_addr(iommu_pt->domain);
498}
499
500/*
501 * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
502 * @mmu - Pointer to mmu structure
503 * @hostptr - Pointer to the IOMMU register map. This is used to match
504 * the iommu device whose lsb value is to be returned
505 * @ctx_id - The context bank whose lsb valus is to be returned
506 * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
507 * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
508 * are only programmed once in the beginning when a domain is attached
509 * does not change.
510 */
511static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
512 unsigned int unit_id,
513 enum kgsl_iommu_context_id ctx_id)
514{
515 struct kgsl_iommu *iommu = mmu->priv;
516 int i, j;
517 for (i = 0; i < iommu->unit_count; i++) {
518 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
519 for (j = 0; j < iommu_unit->dev_count; j++)
520 if (unit_id == i &&
521 ctx_id == iommu_unit->dev[j].ctx_id)
522 return iommu_unit->dev[j].pt_lsb;
523 }
524 return 0;
525}
526
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600527static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600528 struct kgsl_pagetable *pagetable)
529{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600530 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600531 struct kgsl_iommu *iommu = mmu->priv;
532 struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600533 /* page table not current, then setup mmu to use new
534 * specified page table
535 */
536 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600537 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600538 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600539 /* force tlb flush if asid is reused */
540 if (iommu->asid_reuse &&
541 (KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
542 flags |= KGSL_MMUFLAGS_TLBFLUSH;
543 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
544 mmu->device->id);
545 kgsl_setstate(mmu, KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600546 }
547 }
548}
549
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600550static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600551{
552 /*
553 * intialize device mmu
554 *
555 * call this with the global lock held
556 */
557 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600558 struct kgsl_iommu *iommu;
559
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600560 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
561 if (!iommu) {
562 KGSL_CORE_ERR("kzalloc(%d) failed\n",
563 sizeof(struct kgsl_iommu));
564 return -ENOMEM;
565 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600566 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
567 sizeof(unsigned long), GFP_KERNEL);
568 if (!iommu->asids) {
569 KGSL_CORE_ERR("kzalloc(%d) failed\n",
570 sizeof(struct kgsl_iommu));
571 status = -ENOMEM;
572 goto done;
573 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600574
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600575 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600576 status = kgsl_get_iommu_ctxt(mmu);
577 if (status)
578 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600579 status = kgsl_set_register_map(mmu);
580 if (status)
581 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600582
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600583 /* A nop is required in an indirect buffer when switching
584 * pagetables in-stream */
585 kgsl_sharedmem_writel(&mmu->setstate_memory,
586 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
587 cp_nop_packet(1));
588
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600589 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600590 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600591done:
592 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600593 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600594 kfree(iommu);
595 mmu->priv = NULL;
596 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600597 return status;
598}
599
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600600/*
601 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
602 * for iommu. This function is only called once during first start, successive
603 * start do not call this funciton.
604 * @mmu - Pointer to mmu structure
605 *
606 * Create the initial defaultpagetable and setup the iommu mappings to it
607 * Return - 0 on success else error code
608 */
609static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
610{
611 int status = 0;
612 int i = 0;
613 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600614 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600615
616 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
617 /* Return error if the default pagetable doesn't exist */
618 if (mmu->defaultpagetable == NULL) {
619 status = -ENOMEM;
620 goto err;
621 }
622 /* Map the IOMMU regsiters to only defaultpagetable */
623 for (i = 0; i < iommu->unit_count; i++) {
624 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
625 status = kgsl_mmu_map(mmu->defaultpagetable,
626 &(iommu->iommu_units[i].reg_map),
627 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
628 if (status) {
629 iommu->iommu_units[i].reg_map.priv &=
630 ~KGSL_MEMFLAGS_GLOBAL;
631 goto err;
632 }
633 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600634 /*
635 * The dafault pagetable always has asid 0 assigned by the iommu driver
636 * and asid 1 is assigned to the private context.
637 */
638 iommu_pt = mmu->defaultpagetable->priv;
639 iommu_pt->asid = 0;
640 set_bit(0, iommu->asids);
641 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600642 return status;
643err:
644 for (i--; i >= 0; i--) {
645 kgsl_mmu_unmap(mmu->defaultpagetable,
646 &(iommu->iommu_units[i].reg_map));
647 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
648 }
649 if (mmu->defaultpagetable) {
650 kgsl_mmu_putpagetable(mmu->defaultpagetable);
651 mmu->defaultpagetable = NULL;
652 }
653 return status;
654}
655
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600656static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600657{
658 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600659 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600660 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600661
662 if (mmu->flags & KGSL_FLAGS_STARTED)
663 return 0;
664
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600665 if (mmu->defaultpagetable == NULL) {
666 status = kgsl_iommu_setup_defaultpagetable(mmu);
667 if (status)
668 return -ENOMEM;
669 }
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600670 /* We use the GPU MMU to control access to IOMMU registers on a225,
671 * hence we still keep the MMU active on a225 */
672 if (adreno_is_a225(ADRENO_DEVICE(mmu->device))) {
673 struct kgsl_mh *mh = &(mmu->device->mh);
674 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
675 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
676 mh->mpu_base +
Shubhraprakash Dasc6e21012012-05-11 17:24:51 -0600677 iommu->iommu_units
678 [iommu->unit_count - 1].reg_map.gpuaddr -
679 PAGE_SIZE);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600680 } else {
681 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
682 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600683
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600684 mmu->hwpagetable = mmu->defaultpagetable;
685
686 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600687 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600688 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600689 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600690 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600691 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600692 if (status) {
693 KGSL_CORE_ERR("clk enable failed\n");
694 goto done;
695 }
696 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
697 if (status) {
698 KGSL_CORE_ERR("clk enable failed\n");
699 goto done;
700 }
701 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
702 * that value should not change when we change pagetables, so while
703 * changing pagetables we can use this lsb value of the pagetable w/o
704 * having to read it again
705 */
706 for (i = 0; i < iommu->unit_count; i++) {
707 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
708 for (j = 0; j < iommu_unit->dev_count; j++)
709 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
710 KGSL_IOMMU_GET_IOMMU_REG(
711 iommu_unit->reg_map.hostptr,
712 iommu_unit->dev[j].ctx_id,
713 TTBR0));
714 }
715 iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
716 iommu->iommu_units[0].reg_map.hostptr,
717 KGSL_IOMMU_CONTEXT_USER,
718 CONTEXTIDR);
719
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600720 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600721 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600722
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600723done:
724 if (status) {
725 kgsl_iommu_disable_clk(mmu);
726 kgsl_detach_pagetable_iommu_domain(mmu);
727 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600728 return status;
729}
730
731static int
732kgsl_iommu_unmap(void *mmu_specific_pt,
733 struct kgsl_memdesc *memdesc)
734{
735 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600736 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600737 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600738
739 /* All GPU addresses as assigned are page aligned, but some
740 functions purturb the gpuaddr with an offset, so apply the
741 mask here to make sure we have the right address */
742
743 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
744
745 if (range == 0 || gpuaddr == 0)
746 return 0;
747
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600748 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600749 if (ret)
750 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600751 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600752 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600753
754 return 0;
755}
756
757static int
758kgsl_iommu_map(void *mmu_specific_pt,
759 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600760 unsigned int protflags,
761 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600762{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600763 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600764 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600765 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600766 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600767
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600768 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600769
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600770
Jordan Croused17e9aa2011-10-12 16:57:48 -0600771 iommu_virt_addr = memdesc->gpuaddr;
772
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600773 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600774 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600775 if (ret) {
776 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600777 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600778 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600779 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600780 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600781 }
782
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600783#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
784 /*
785 * Flushing only required if per process pagetables are used. With
786 * global case, flushing will happen inside iommu_map function
787 */
788 if (!ret)
789 *tlb_flags = UINT_MAX;
790#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600791 return ret;
792}
793
Shubhraprakash Das79447952012-04-26 18:12:23 -0600794static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600795{
796 /*
797 * stop device mmu
798 *
799 * call this with the global lock held
800 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600801
802 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600803 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600804 /* detach iommu attachment */
805 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600806 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600807
808 mmu->flags &= ~KGSL_FLAGS_STARTED;
809 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600810}
811
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600812static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600813{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600814 struct kgsl_iommu *iommu = mmu->priv;
815 int i;
816 for (i = 0; i < iommu->unit_count; i++) {
817 if (iommu->iommu_units[i].reg_map.gpuaddr)
818 kgsl_mmu_unmap(mmu->defaultpagetable,
819 &(iommu->iommu_units[i].reg_map));
820 if (iommu->iommu_units[i].reg_map.hostptr)
821 iounmap(iommu->iommu_units[i].reg_map.hostptr);
822 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
823 iommu->iommu_units[i].reg_map.sglen);
824 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600825 if (mmu->defaultpagetable)
826 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600827 kfree(iommu->asids);
828 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600829
830 return 0;
831}
832
833static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600834kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600835{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600836 unsigned int pt_base;
837 struct kgsl_iommu *iommu = mmu->priv;
838 /* Return the current pt base by reading IOMMU pt_base register */
839 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
840 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
841 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
842 KGSL_IOMMU_TTBR0);
843 kgsl_iommu_disable_clk(mmu);
844 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
845 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600846}
847
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600848/*
849 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
850 * pagetable
851 * @mmu - Pointer to mmu structure
852 *
853 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
854 * are unique identifiers for pagetable that can be used to selectively flush
855 * tlb entries of the IOMMU unit.
856 * Return - asid to be used with the IOMMU domain
857 */
858static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
859{
860 struct kgsl_iommu *iommu = mmu->priv;
861 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
862
863 /*
864 * If the iommu pagetable does not have any asid assigned and is not the
865 * default pagetable then assign asid.
866 */
867 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
868 iommu_pt->asid = find_first_zero_bit(iommu->asids,
869 KGSL_IOMMU_MAX_ASIDS);
870 /* No free bits means reuse asid */
871 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
872 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
873 iommu->asid_reuse++;
874 }
875 set_bit(iommu_pt->asid, iommu->asids);
876 /*
877 * Store pointer to asids list so that during pagetable destroy
878 * the asid assigned to this pagetable may be cleared
879 */
880 iommu_pt->iommu = iommu;
881 }
882 /* Return the asid + the constant part of asid that never changes */
883 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
884 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
885 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
886 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
887}
888
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600889/*
890 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
891 * of the primary context bank
892 * @mmu - Pointer to mmu structure
893 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
894 * flushed or both
895 *
896 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
897 * do both by doing direct register writes to the IOMMu registers through the
898 * cpu
899 * Return - void
900 */
901static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
902 uint32_t flags)
903{
904 struct kgsl_iommu *iommu = mmu->priv;
905 int temp;
906 int i;
907 unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
908 mmu->hwpagetable);
909 unsigned int pt_val;
910
911 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
912 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
913 return;
914 }
915 /* Mask off the lsb of the pt base address since lsb will not change */
916 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
917 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
918 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
919 for (i = 0; i < iommu->unit_count; i++) {
920 /* get the lsb value which should not change when
921 * changing ttbr0 */
922 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
923 KGSL_IOMMU_CONTEXT_USER);
924 pt_val += pt_base;
925
926 KGSL_IOMMU_SET_IOMMU_REG(
927 iommu->iommu_units[i].reg_map.hostptr,
928 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
929
930 mb();
931 temp = KGSL_IOMMU_GET_IOMMU_REG(
932 iommu->iommu_units[i].reg_map.hostptr,
933 KGSL_IOMMU_CONTEXT_USER, TTBR0);
934 /* Set asid */
935 KGSL_IOMMU_SET_IOMMU_REG(
936 iommu->iommu_units[i].reg_map.hostptr,
937 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
938 kgsl_iommu_get_hwpagetable_asid(mmu));
939 mb();
940 temp = KGSL_IOMMU_GET_IOMMU_REG(
941 iommu->iommu_units[i].reg_map.hostptr,
942 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
943 }
944 }
945 /* Flush tlb */
946 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
947 for (i = 0; i < iommu->unit_count; i++) {
948 KGSL_IOMMU_SET_IOMMU_REG(
949 iommu->iommu_units[i].reg_map.hostptr,
950 KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
951 kgsl_iommu_get_hwpagetable_asid(mmu));
952 mb();
953 }
954 }
955 /* Disable smmu clock */
956 kgsl_iommu_disable_clk(mmu);
957}
958
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -0600959/*
960 * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
961 * the address of memory descriptors which map the IOMMU registers
962 * @mmu - Pointer to mmu structure
963 * @reg_map_desc - Out parameter in which the address of the array containing
964 * pointers to register map descriptors is returned. The caller is supposed
965 * to free this array
966 *
967 * Return - The number of iommu units which is also the number of register
968 * mapped descriptor arrays which the out parameter will have
969 */
970static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
971 void **reg_map_desc)
972{
973 struct kgsl_iommu *iommu = mmu->priv;
974 void **reg_desc_ptr;
975 int i;
976
977 /*
978 * Alocate array of pointers that will hold address of the register map
979 * descriptors
980 */
981 reg_desc_ptr = kmalloc(iommu->unit_count *
982 sizeof(struct kgsl_memdesc *), GFP_KERNEL);
983 if (!reg_desc_ptr) {
984 KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
985 iommu->unit_count * sizeof(struct kgsl_memdesc *));
986 return -ENOMEM;
987 }
988
989 for (i = 0; i < iommu->unit_count; i++)
990 reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
991
992 *reg_map_desc = reg_desc_ptr;
993 return i;
994}
995
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600996struct kgsl_mmu_ops iommu_ops = {
997 .mmu_init = kgsl_iommu_init,
998 .mmu_close = kgsl_iommu_close,
999 .mmu_start = kgsl_iommu_start,
1000 .mmu_stop = kgsl_iommu_stop,
1001 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001002 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001003 .mmu_pagefault = NULL,
1004 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001005 .mmu_enable_clk = kgsl_iommu_enable_clk,
1006 .mmu_disable_clk = kgsl_iommu_disable_clk,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001007 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001008 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001009 .mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001010};
1011
1012struct kgsl_mmu_pt_ops iommu_pt_ops = {
1013 .mmu_map = kgsl_iommu_map,
1014 .mmu_unmap = kgsl_iommu_unmap,
1015 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1016 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
1017 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das5a610b52012-05-09 17:31:54 -06001018 .mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001019};