blob: febb265a94190adcbc2a177d5136e78f42659d86 [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 }
Steve Mucklef132c6c2012-06-06 18:30:57 -0700238 iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
239 MSM_IOMMU_DOMAIN_PT_CACHEABLE);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600240 if (!iommu_pt->domain) {
241 KGSL_CORE_ERR("Failed to create iommu domain\n");
242 kfree(iommu_pt);
243 return NULL;
Jordan Crouse95b68472012-05-25 10:25:01 -0600244 } else {
245 iommu_set_fault_handler(iommu_pt->domain,
246 kgsl_iommu_fault_handler);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600247 }
Jordan Crouse95b68472012-05-25 10:25:01 -0600248
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600249 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600250}
251
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600252/*
253 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
254 * pagetable
255 * @mmu - Pointer to the device mmu structure
256 * @priv - Flag indicating whether the private or user context is to be
257 * detached
258 *
259 * Detach the IOMMU unit with the domain that is contained in the
260 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
261 * in use because the PTBR will not be set after a detach
262 * Return - void
263 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600264static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
265{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600266 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600267 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600268 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600269
270 BUG_ON(mmu->hwpagetable == NULL);
271 BUG_ON(mmu->hwpagetable->priv == NULL);
272
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600273 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600274
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600275 for (i = 0; i < iommu->unit_count; i++) {
276 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
277 for (j = 0; j < iommu_unit->dev_count; j++) {
278 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600279 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600280 iommu_unit->dev[j].dev);
281 iommu_unit->dev[j].attached = false;
282 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
283 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600284 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600285 }
286 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600287 }
288}
289
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600290/*
291 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
292 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
293 * setup other IOMMU registers for the device so that it becomes
294 * active
295 * @mmu - Pointer to the device mmu structure
296 * @priv - Flag indicating whether the private or user context is to be
297 * attached
298 *
299 * Attach the IOMMU unit with the domain that is contained in the
300 * hwpagetable of the given mmu.
301 * Return - 0 on success else error code
302 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600303static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
304{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600305 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600306 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600307 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600308
309 BUG_ON(mmu->hwpagetable == NULL);
310 BUG_ON(mmu->hwpagetable->priv == NULL);
311
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600312 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600313
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600314 /*
315 * Loop through all the iommu devcies under all iommu units and
316 * attach the domain
317 */
318 for (i = 0; i < iommu->unit_count; i++) {
319 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
320 for (j = 0; j < iommu_unit->dev_count; j++) {
321 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600322 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600323 iommu_unit->dev[j].dev);
324 if (ret) {
325 KGSL_MEM_ERR(mmu->device,
326 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700327 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600328 goto done;
329 }
330 iommu_unit->dev[j].attached = true;
331 KGSL_MEM_INFO(mmu->device,
332 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600333 iommu_pt->domain, iommu_unit->dev[j].dev,
334 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700335 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600336 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600337 }
338done:
339 return ret;
340}
341
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600342/*
343 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
344 * @mmu - Pointer to mmu device
345 * data - Pointer to the platform data containing information about
346 * iommu devices for one iommu unit
347 * unit_id - The IOMMU unit number. This is not a specific ID but just
348 * a serial number. The serial numbers are treated as ID's of the
349 * IOMMU units
350 *
351 * Return - 0 on success else error code
352 */
353static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
354 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700355{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600356 struct kgsl_iommu *iommu = mmu->priv;
357 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700358 int i;
359
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600360 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
361 KGSL_CORE_ERR("Too many iommu devices defined for an "
362 "IOMMU unit\n");
363 return -EINVAL;
364 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700365
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600366 for (i = 0; i < data->iommu_ctx_count; i++) {
367 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700368 continue;
369
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600370 iommu_unit->dev[iommu_unit->dev_count].dev =
371 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
372 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
373 KGSL_CORE_ERR("Failed to get iommu dev handle for "
374 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700375 return -EINVAL;
376 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600377 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
378 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
379 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
380 data->iommu_ctxs[i].ctx_id);
381 return -EINVAL;
382 }
383 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
384 data->iommu_ctxs[i].ctx_id;
Jordan Crouse95b68472012-05-25 10:25:01 -0600385 iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
386
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600387 KGSL_DRV_INFO(mmu->device,
388 "Obtained dev handle %p for iommu context %s\n",
389 iommu_unit->dev[iommu_unit->dev_count].dev,
390 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700391
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600392 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700393 }
394
395 return 0;
396}
397
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600398/*
399 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
400 * @mmu - Pointer to mmu device
401 *
402 * Get the device pointers for the IOMMU user and priv contexts of the
403 * kgsl device
404 * Return - 0 on success else error code
405 */
406static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600407{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600408 struct platform_device *pdev =
409 container_of(mmu->device->parentdev, struct platform_device,
410 dev);
411 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
412 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700413 int i, ret = 0;
414
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600415 /* Go through the IOMMU data and get all the context devices */
416 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
417 KGSL_CORE_ERR("Too many IOMMU units defined\n");
418 ret = -EINVAL;
419 goto done;
420 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700421
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600422 for (i = 0; i < pdata_dev->iommu_count; i++) {
423 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700424 if (ret)
425 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600426 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600427 iommu->unit_count = pdata_dev->iommu_count;
428done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700429 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600430}
431
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600432/*
433 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
434 * of the respective iommu units
435 * @mmu - Pointer to mmu structure
436 *
437 * Return - 0 on success else error code
438 */
439static int kgsl_set_register_map(struct kgsl_mmu *mmu)
440{
441 struct platform_device *pdev =
442 container_of(mmu->device->parentdev, struct platform_device,
443 dev);
444 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
445 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
446 struct kgsl_iommu_unit *iommu_unit;
447 int i = 0, ret = 0;
448
449 for (; i < pdata_dev->iommu_count; i++) {
450 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
451 iommu_unit = &iommu->iommu_units[i];
452 /* set up the IOMMU register map for the given IOMMU unit */
453 if (!data.physstart || !data.physend) {
454 KGSL_CORE_ERR("The register range for IOMMU unit not"
455 " specified\n");
456 ret = -EINVAL;
457 goto err;
458 }
459 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
460 data.physend - data.physstart + 1);
461 if (!iommu_unit->reg_map.hostptr) {
462 KGSL_CORE_ERR("Failed to map SMMU register address "
463 "space from %x to %x\n", data.physstart,
464 data.physend - data.physstart + 1);
465 ret = -ENOMEM;
466 i--;
467 goto err;
468 }
469 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
470 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600471 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
472 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600473 }
474 iommu->unit_count = pdata_dev->iommu_count;
475 return ret;
476err:
477 /* Unmap any mapped IOMMU regions */
478 for (; i >= 0; i--) {
479 iommu_unit = &iommu->iommu_units[i];
480 iounmap(iommu_unit->reg_map.hostptr);
481 iommu_unit->reg_map.size = 0;
482 iommu_unit->reg_map.physaddr = 0;
483 }
484 return ret;
485}
486
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600487/*
488 * kgsl_iommu_pt_get_base_addr - Get the address of the pagetable that the
489 * IOMMU ttbr0 register is programmed with
490 * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
491 *
492 * Return - actual pagetable address that the ttbr0 register is programmed
493 * with
494 */
495static unsigned int kgsl_iommu_pt_get_base_addr(struct kgsl_pagetable *pt)
496{
497 struct kgsl_iommu_pt *iommu_pt = pt->priv;
498 return iommu_get_pt_base_addr(iommu_pt->domain);
499}
500
501/*
502 * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
503 * @mmu - Pointer to mmu structure
504 * @hostptr - Pointer to the IOMMU register map. This is used to match
505 * the iommu device whose lsb value is to be returned
506 * @ctx_id - The context bank whose lsb valus is to be returned
507 * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
508 * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
509 * are only programmed once in the beginning when a domain is attached
510 * does not change.
511 */
512static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
513 unsigned int unit_id,
514 enum kgsl_iommu_context_id ctx_id)
515{
516 struct kgsl_iommu *iommu = mmu->priv;
517 int i, j;
518 for (i = 0; i < iommu->unit_count; i++) {
519 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
520 for (j = 0; j < iommu_unit->dev_count; j++)
521 if (unit_id == i &&
522 ctx_id == iommu_unit->dev[j].ctx_id)
523 return iommu_unit->dev[j].pt_lsb;
524 }
525 return 0;
526}
527
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600528static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600529 struct kgsl_pagetable *pagetable)
530{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600531 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600532 struct kgsl_iommu *iommu = mmu->priv;
533 struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600534 /* page table not current, then setup mmu to use new
535 * specified page table
536 */
537 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600538 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600539 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600540 /* force tlb flush if asid is reused */
541 if (iommu->asid_reuse &&
542 (KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
543 flags |= KGSL_MMUFLAGS_TLBFLUSH;
544 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
545 mmu->device->id);
546 kgsl_setstate(mmu, KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600547 }
548 }
549}
550
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600551static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600552{
553 /*
554 * intialize device mmu
555 *
556 * call this with the global lock held
557 */
558 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600559 struct kgsl_iommu *iommu;
560
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600561 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
562 if (!iommu) {
563 KGSL_CORE_ERR("kzalloc(%d) failed\n",
564 sizeof(struct kgsl_iommu));
565 return -ENOMEM;
566 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600567 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
568 sizeof(unsigned long), GFP_KERNEL);
569 if (!iommu->asids) {
570 KGSL_CORE_ERR("kzalloc(%d) failed\n",
571 sizeof(struct kgsl_iommu));
572 status = -ENOMEM;
573 goto done;
574 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600575
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600576 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600577 status = kgsl_get_iommu_ctxt(mmu);
578 if (status)
579 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600580 status = kgsl_set_register_map(mmu);
581 if (status)
582 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600583
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600584 /* A nop is required in an indirect buffer when switching
585 * pagetables in-stream */
586 kgsl_sharedmem_writel(&mmu->setstate_memory,
587 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
588 cp_nop_packet(1));
589
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600590 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600591 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600592done:
593 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600594 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600595 kfree(iommu);
596 mmu->priv = NULL;
597 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600598 return status;
599}
600
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600601/*
602 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
603 * for iommu. This function is only called once during first start, successive
604 * start do not call this funciton.
605 * @mmu - Pointer to mmu structure
606 *
607 * Create the initial defaultpagetable and setup the iommu mappings to it
608 * Return - 0 on success else error code
609 */
610static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
611{
612 int status = 0;
613 int i = 0;
614 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600615 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600616
617 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
618 /* Return error if the default pagetable doesn't exist */
619 if (mmu->defaultpagetable == NULL) {
620 status = -ENOMEM;
621 goto err;
622 }
623 /* Map the IOMMU regsiters to only defaultpagetable */
624 for (i = 0; i < iommu->unit_count; i++) {
625 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
626 status = kgsl_mmu_map(mmu->defaultpagetable,
627 &(iommu->iommu_units[i].reg_map),
628 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
629 if (status) {
630 iommu->iommu_units[i].reg_map.priv &=
631 ~KGSL_MEMFLAGS_GLOBAL;
632 goto err;
633 }
634 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600635 /*
636 * The dafault pagetable always has asid 0 assigned by the iommu driver
637 * and asid 1 is assigned to the private context.
638 */
639 iommu_pt = mmu->defaultpagetable->priv;
640 iommu_pt->asid = 0;
641 set_bit(0, iommu->asids);
642 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600643 return status;
644err:
645 for (i--; i >= 0; i--) {
646 kgsl_mmu_unmap(mmu->defaultpagetable,
647 &(iommu->iommu_units[i].reg_map));
648 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
649 }
650 if (mmu->defaultpagetable) {
651 kgsl_mmu_putpagetable(mmu->defaultpagetable);
652 mmu->defaultpagetable = NULL;
653 }
654 return status;
655}
656
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600657static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600658{
659 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600660 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600661 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600662
663 if (mmu->flags & KGSL_FLAGS_STARTED)
664 return 0;
665
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600666 if (mmu->defaultpagetable == NULL) {
667 status = kgsl_iommu_setup_defaultpagetable(mmu);
668 if (status)
669 return -ENOMEM;
670 }
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600671 /* We use the GPU MMU to control access to IOMMU registers on a225,
672 * hence we still keep the MMU active on a225 */
673 if (adreno_is_a225(ADRENO_DEVICE(mmu->device))) {
674 struct kgsl_mh *mh = &(mmu->device->mh);
675 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
676 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
677 mh->mpu_base +
Shubhraprakash Dasc6e21012012-05-11 17:24:51 -0600678 iommu->iommu_units
679 [iommu->unit_count - 1].reg_map.gpuaddr -
680 PAGE_SIZE);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600681 } else {
682 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
683 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600684
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600685 mmu->hwpagetable = mmu->defaultpagetable;
686
687 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600688 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600689 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600690 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600691 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600692 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600693 if (status) {
694 KGSL_CORE_ERR("clk enable failed\n");
695 goto done;
696 }
697 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
698 if (status) {
699 KGSL_CORE_ERR("clk enable failed\n");
700 goto done;
701 }
702 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
703 * that value should not change when we change pagetables, so while
704 * changing pagetables we can use this lsb value of the pagetable w/o
705 * having to read it again
706 */
707 for (i = 0; i < iommu->unit_count; i++) {
708 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
709 for (j = 0; j < iommu_unit->dev_count; j++)
710 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
711 KGSL_IOMMU_GET_IOMMU_REG(
712 iommu_unit->reg_map.hostptr,
713 iommu_unit->dev[j].ctx_id,
714 TTBR0));
715 }
716 iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
717 iommu->iommu_units[0].reg_map.hostptr,
718 KGSL_IOMMU_CONTEXT_USER,
719 CONTEXTIDR);
720
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600721 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600722 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600723
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600724done:
725 if (status) {
726 kgsl_iommu_disable_clk(mmu);
727 kgsl_detach_pagetable_iommu_domain(mmu);
728 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600729 return status;
730}
731
732static int
733kgsl_iommu_unmap(void *mmu_specific_pt,
734 struct kgsl_memdesc *memdesc)
735{
736 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600737 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600738 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600739
740 /* All GPU addresses as assigned are page aligned, but some
741 functions purturb the gpuaddr with an offset, so apply the
742 mask here to make sure we have the right address */
743
744 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
745
746 if (range == 0 || gpuaddr == 0)
747 return 0;
748
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600749 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600750 if (ret)
751 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600752 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600753 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600754
755 return 0;
756}
757
758static int
759kgsl_iommu_map(void *mmu_specific_pt,
760 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600761 unsigned int protflags,
762 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600763{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600764 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600765 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600766 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600767 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600768
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600769 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600770
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600771
Jordan Croused17e9aa2011-10-12 16:57:48 -0600772 iommu_virt_addr = memdesc->gpuaddr;
773
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600774 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600775 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600776 if (ret) {
777 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600778 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600779 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600780 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600781 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600782 }
783
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600784#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
785 /*
786 * Flushing only required if per process pagetables are used. With
787 * global case, flushing will happen inside iommu_map function
788 */
789 if (!ret)
790 *tlb_flags = UINT_MAX;
791#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600792 return ret;
793}
794
Shubhraprakash Das79447952012-04-26 18:12:23 -0600795static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600796{
797 /*
798 * stop device mmu
799 *
800 * call this with the global lock held
801 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600802
803 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600804 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600805 /* detach iommu attachment */
806 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600807 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600808
809 mmu->flags &= ~KGSL_FLAGS_STARTED;
810 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600811}
812
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600813static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600814{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600815 struct kgsl_iommu *iommu = mmu->priv;
816 int i;
817 for (i = 0; i < iommu->unit_count; i++) {
818 if (iommu->iommu_units[i].reg_map.gpuaddr)
819 kgsl_mmu_unmap(mmu->defaultpagetable,
820 &(iommu->iommu_units[i].reg_map));
821 if (iommu->iommu_units[i].reg_map.hostptr)
822 iounmap(iommu->iommu_units[i].reg_map.hostptr);
823 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
824 iommu->iommu_units[i].reg_map.sglen);
825 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600826 if (mmu->defaultpagetable)
827 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600828 kfree(iommu->asids);
829 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600830
831 return 0;
832}
833
834static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600835kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600836{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600837 unsigned int pt_base;
838 struct kgsl_iommu *iommu = mmu->priv;
839 /* Return the current pt base by reading IOMMU pt_base register */
840 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
841 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
842 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
843 KGSL_IOMMU_TTBR0);
844 kgsl_iommu_disable_clk(mmu);
845 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
846 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600847}
848
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600849/*
850 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
851 * pagetable
852 * @mmu - Pointer to mmu structure
853 *
854 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
855 * are unique identifiers for pagetable that can be used to selectively flush
856 * tlb entries of the IOMMU unit.
857 * Return - asid to be used with the IOMMU domain
858 */
859static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
860{
861 struct kgsl_iommu *iommu = mmu->priv;
862 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
863
864 /*
865 * If the iommu pagetable does not have any asid assigned and is not the
866 * default pagetable then assign asid.
867 */
868 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
869 iommu_pt->asid = find_first_zero_bit(iommu->asids,
870 KGSL_IOMMU_MAX_ASIDS);
871 /* No free bits means reuse asid */
872 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
873 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
874 iommu->asid_reuse++;
875 }
876 set_bit(iommu_pt->asid, iommu->asids);
877 /*
878 * Store pointer to asids list so that during pagetable destroy
879 * the asid assigned to this pagetable may be cleared
880 */
881 iommu_pt->iommu = iommu;
882 }
883 /* Return the asid + the constant part of asid that never changes */
884 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
885 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
886 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
887 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
888}
889
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600890/*
891 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
892 * of the primary context bank
893 * @mmu - Pointer to mmu structure
894 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
895 * flushed or both
896 *
897 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
898 * do both by doing direct register writes to the IOMMu registers through the
899 * cpu
900 * Return - void
901 */
902static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
903 uint32_t flags)
904{
905 struct kgsl_iommu *iommu = mmu->priv;
906 int temp;
907 int i;
908 unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
909 mmu->hwpagetable);
910 unsigned int pt_val;
911
912 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
913 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
914 return;
915 }
916 /* Mask off the lsb of the pt base address since lsb will not change */
917 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
918 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
919 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
920 for (i = 0; i < iommu->unit_count; i++) {
921 /* get the lsb value which should not change when
922 * changing ttbr0 */
923 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
924 KGSL_IOMMU_CONTEXT_USER);
925 pt_val += pt_base;
926
927 KGSL_IOMMU_SET_IOMMU_REG(
928 iommu->iommu_units[i].reg_map.hostptr,
929 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
930
931 mb();
932 temp = KGSL_IOMMU_GET_IOMMU_REG(
933 iommu->iommu_units[i].reg_map.hostptr,
934 KGSL_IOMMU_CONTEXT_USER, TTBR0);
935 /* Set asid */
936 KGSL_IOMMU_SET_IOMMU_REG(
937 iommu->iommu_units[i].reg_map.hostptr,
938 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
939 kgsl_iommu_get_hwpagetable_asid(mmu));
940 mb();
941 temp = KGSL_IOMMU_GET_IOMMU_REG(
942 iommu->iommu_units[i].reg_map.hostptr,
943 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
944 }
945 }
946 /* Flush tlb */
947 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
948 for (i = 0; i < iommu->unit_count; i++) {
949 KGSL_IOMMU_SET_IOMMU_REG(
950 iommu->iommu_units[i].reg_map.hostptr,
951 KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
952 kgsl_iommu_get_hwpagetable_asid(mmu));
953 mb();
954 }
955 }
956 /* Disable smmu clock */
957 kgsl_iommu_disable_clk(mmu);
958}
959
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -0600960/*
961 * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
962 * the address of memory descriptors which map the IOMMU registers
963 * @mmu - Pointer to mmu structure
964 * @reg_map_desc - Out parameter in which the address of the array containing
965 * pointers to register map descriptors is returned. The caller is supposed
966 * to free this array
967 *
968 * Return - The number of iommu units which is also the number of register
969 * mapped descriptor arrays which the out parameter will have
970 */
971static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
972 void **reg_map_desc)
973{
974 struct kgsl_iommu *iommu = mmu->priv;
975 void **reg_desc_ptr;
976 int i;
977
978 /*
979 * Alocate array of pointers that will hold address of the register map
980 * descriptors
981 */
982 reg_desc_ptr = kmalloc(iommu->unit_count *
983 sizeof(struct kgsl_memdesc *), GFP_KERNEL);
984 if (!reg_desc_ptr) {
985 KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
986 iommu->unit_count * sizeof(struct kgsl_memdesc *));
987 return -ENOMEM;
988 }
989
990 for (i = 0; i < iommu->unit_count; i++)
991 reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
992
993 *reg_map_desc = reg_desc_ptr;
994 return i;
995}
996
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600997struct kgsl_mmu_ops iommu_ops = {
998 .mmu_init = kgsl_iommu_init,
999 .mmu_close = kgsl_iommu_close,
1000 .mmu_start = kgsl_iommu_start,
1001 .mmu_stop = kgsl_iommu_stop,
1002 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001003 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001004 .mmu_pagefault = NULL,
1005 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001006 .mmu_enable_clk = kgsl_iommu_enable_clk,
1007 .mmu_disable_clk = kgsl_iommu_disable_clk,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001008 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001009 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001010 .mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001011};
1012
1013struct kgsl_mmu_pt_ops iommu_pt_ops = {
1014 .mmu_map = kgsl_iommu_map,
1015 .mmu_unmap = kgsl_iommu_unmap,
1016 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1017 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
1018 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das5a610b52012-05-09 17:31:54 -06001019 .mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001020};