blob: d20cf7e0d917e6a4cbd8b0809a2f9c23cc92ee53 [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>
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -060020#include <mach/socinfo.h>
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060021
22#include "kgsl.h"
23#include "kgsl_device.h"
24#include "kgsl_mmu.h"
25#include "kgsl_sharedmem.h"
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060026#include "kgsl_iommu.h"
Shubhraprakash Dase9541a32012-05-09 22:25:55 -060027#include "adreno_pm4types.h"
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -060028#include "adreno.h"
Jordan Crouse95b68472012-05-25 10:25:01 -060029#include "kgsl_trace.h"
30
31static struct kgsl_iommu_unit *get_iommu_unit(struct device *dev)
32{
33 int i, j, k;
34
35 for (i = 0; i < KGSL_DEVICE_MAX; i++) {
36 struct kgsl_mmu *mmu;
37 struct kgsl_iommu *iommu;
38
39 if (kgsl_driver.devp[i] == NULL)
40 continue;
41
42 mmu = kgsl_get_mmu(kgsl_driver.devp[i]);
43 if (mmu == NULL || mmu->priv == NULL)
44 continue;
45
46 iommu = mmu->priv;
47
48 for (j = 0; j < iommu->unit_count; j++) {
49 struct kgsl_iommu_unit *iommu_unit =
50 &iommu->iommu_units[j];
51 for (k = 0; k < iommu_unit->dev_count; k++) {
52 if (iommu_unit->dev[k].dev == dev)
53 return iommu_unit;
54 }
55 }
56 }
57
58 return NULL;
59}
60
61static struct kgsl_iommu_device *get_iommu_device(struct kgsl_iommu_unit *unit,
62 struct device *dev)
63{
64 int k;
65
66 for (k = 0; unit && k < unit->dev_count; k++) {
67 if (unit->dev[k].dev == dev)
68 return &(unit->dev[k]);
69 }
70
71 return NULL;
72}
73
74static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
75 struct device *dev, unsigned long addr, int flags)
76{
77 struct kgsl_iommu_unit *iommu_unit = get_iommu_unit(dev);
78 struct kgsl_iommu_device *iommu_dev = get_iommu_device(iommu_unit, dev);
79 unsigned int ptbase, fsr;
80
81 if (!iommu_dev) {
82 KGSL_CORE_ERR("Invalid IOMMU device %p\n", dev);
83 return -ENOSYS;
84 }
85
Shubhraprakash Das190553a2012-06-11 15:17:59 -060086 ptbase = KGSL_IOMMU_GET_IOMMU_REG(iommu_unit->reg_map.hostptr,
87 iommu_dev->ctx_id, TTBR0);
Jordan Crouse95b68472012-05-25 10:25:01 -060088
89 fsr = KGSL_IOMMU_GET_IOMMU_REG(iommu_unit->reg_map.hostptr,
90 iommu_dev->ctx_id, FSR);
91
92 KGSL_MEM_CRIT(iommu_dev->kgsldev,
93 "GPU PAGE FAULT: addr = %lX pid = %d\n",
94 addr, kgsl_mmu_get_ptname_from_ptbase(ptbase));
95 KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
96 iommu_dev->ctx_id, fsr);
97
98 trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
99 kgsl_mmu_get_ptname_from_ptbase(ptbase), 0);
100
101 return 0;
102}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600103
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600104/*
105 * kgsl_iommu_disable_clk - Disable iommu clocks
106 * @mmu - Pointer to mmu structure
107 *
108 * Disables iommu clocks
109 * Return - void
110 */
111static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
112{
113 struct kgsl_iommu *iommu = mmu->priv;
114 struct msm_iommu_drvdata *iommu_drvdata;
115 int i, j;
116
117 for (i = 0; i < iommu->unit_count; i++) {
118 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
119 for (j = 0; j < iommu_unit->dev_count; j++) {
120 if (!iommu_unit->dev[j].clk_enabled)
121 continue;
122 iommu_drvdata = dev_get_drvdata(
123 iommu_unit->dev[j].dev->parent);
124 if (iommu_drvdata->clk)
125 clk_disable_unprepare(iommu_drvdata->clk);
126 clk_disable_unprepare(iommu_drvdata->pclk);
127 iommu_unit->dev[j].clk_enabled = false;
128 }
129 }
130}
131
132/*
133 * kgsl_iommu_enable_clk - Enable iommu clocks
134 * @mmu - Pointer to mmu structure
135 * @ctx_id - The context bank whose clocks are to be turned on
136 *
137 * Enables iommu clocks of a given context
138 * Return: 0 on success else error code
139 */
140static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
141 int ctx_id)
142{
143 int ret = 0;
144 int i, j;
145 struct kgsl_iommu *iommu = mmu->priv;
146 struct msm_iommu_drvdata *iommu_drvdata;
147
148 for (i = 0; i < iommu->unit_count; i++) {
149 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
150 for (j = 0; j < iommu_unit->dev_count; j++) {
151 if (iommu_unit->dev[j].clk_enabled ||
152 ctx_id != iommu_unit->dev[j].ctx_id)
153 continue;
154 iommu_drvdata =
155 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
156 ret = clk_prepare_enable(iommu_drvdata->pclk);
157 if (ret)
158 goto done;
159 if (iommu_drvdata->clk) {
160 ret = clk_prepare_enable(iommu_drvdata->clk);
161 if (ret) {
162 clk_disable_unprepare(
163 iommu_drvdata->pclk);
164 goto done;
165 }
166 }
167 iommu_unit->dev[j].clk_enabled = true;
168 }
169 }
170done:
171 if (ret)
172 kgsl_iommu_disable_clk(mmu);
173 return ret;
174}
175
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600176/*
177 * kgsl_iommu_pt_equal - Check if pagetables are equal
178 * @pt - Pointer to pagetable
179 * @pt_base - Address of a pagetable that the IOMMU register is
180 * programmed with
181 *
182 * Checks whether the pt_base is equal to the base address of
183 * the pagetable which is contained in the pt structure
184 * Return - Non-zero if the pagetable addresses are equal else 0
185 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600186static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
187 unsigned int pt_base)
188{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600189 struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
190 unsigned int domain_ptbase = iommu_pt ?
191 iommu_get_pt_base_addr(iommu_pt->domain) : 0;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600192 /* Only compare the valid address bits of the pt_base */
193 domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
194 KGSL_IOMMU_TTBR0_PA_SHIFT);
195 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
196 KGSL_IOMMU_TTBR0_PA_SHIFT);
197 return domain_ptbase && pt_base &&
198 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600199}
200
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600201/*
202 * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
203 * @mmu_specific_pt - Pointer to pagetable which is to be freed
204 *
205 * Return - void
206 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600207static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
208{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600209 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
210 if (iommu_pt->domain)
211 iommu_domain_free(iommu_pt->domain);
212 if (iommu_pt->iommu) {
213 if ((KGSL_IOMMU_ASID_REUSE == iommu_pt->asid) &&
214 iommu_pt->iommu->asid_reuse)
215 iommu_pt->iommu->asid_reuse--;
216 if (!iommu_pt->iommu->asid_reuse ||
217 (KGSL_IOMMU_ASID_REUSE != iommu_pt->asid))
218 clear_bit(iommu_pt->asid, iommu_pt->iommu->asids);
219 }
220 kfree(iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600221}
222
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600223/*
224 * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
225 *
226 * Allocate memory to hold a pagetable and allocate the IOMMU
227 * domain which is the actual IOMMU pagetable
228 * Return - void
229 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600230void *kgsl_iommu_create_pagetable(void)
231{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600232 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600233
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600234 iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
235 if (!iommu_pt) {
236 KGSL_CORE_ERR("kzalloc(%d) failed\n",
237 sizeof(struct kgsl_iommu_pt));
238 return NULL;
239 }
Steve Mucklef132c6c2012-06-06 18:30:57 -0700240 iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
241 MSM_IOMMU_DOMAIN_PT_CACHEABLE);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600242 if (!iommu_pt->domain) {
243 KGSL_CORE_ERR("Failed to create iommu domain\n");
244 kfree(iommu_pt);
245 return NULL;
Jordan Crouse95b68472012-05-25 10:25:01 -0600246 } else {
247 iommu_set_fault_handler(iommu_pt->domain,
248 kgsl_iommu_fault_handler);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600249 }
Jordan Crouse95b68472012-05-25 10:25:01 -0600250
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600251 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600252}
253
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600254/*
255 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
256 * pagetable
257 * @mmu - Pointer to the device mmu structure
258 * @priv - Flag indicating whether the private or user context is to be
259 * detached
260 *
261 * Detach the IOMMU unit with the domain that is contained in the
262 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
263 * in use because the PTBR will not be set after a detach
264 * Return - void
265 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600266static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
267{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600268 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600269 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600270 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600271
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600272 for (i = 0; i < iommu->unit_count; i++) {
273 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600274 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600275 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600276 /*
277 * If there is a 2nd default pagetable then priv domain
278 * is attached with this pagetable
279 */
280 if (mmu->priv_bank_table &&
281 (KGSL_IOMMU_CONTEXT_PRIV == j))
282 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600283 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600284 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600285 iommu_unit->dev[j].dev);
286 iommu_unit->dev[j].attached = false;
287 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
288 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600289 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600290 }
291 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600292 }
293}
294
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600295/*
296 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
297 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
298 * setup other IOMMU registers for the device so that it becomes
299 * active
300 * @mmu - Pointer to the device mmu structure
301 * @priv - Flag indicating whether the private or user context is to be
302 * attached
303 *
304 * Attach the IOMMU unit with the domain that is contained in the
305 * hwpagetable of the given mmu.
306 * Return - 0 on success else error code
307 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600308static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
309{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600310 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600311 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600312 int i, j, ret = 0;
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];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600320 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600321 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600322 /*
323 * If there is a 2nd default pagetable then priv domain
324 * is attached to this pagetable
325 */
326 if (mmu->priv_bank_table &&
327 (KGSL_IOMMU_CONTEXT_PRIV == j))
328 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600329 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600330 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600331 iommu_unit->dev[j].dev);
332 if (ret) {
333 KGSL_MEM_ERR(mmu->device,
334 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700335 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600336 goto done;
337 }
338 iommu_unit->dev[j].attached = true;
339 KGSL_MEM_INFO(mmu->device,
340 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600341 iommu_pt->domain, iommu_unit->dev[j].dev,
342 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700343 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600344 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600345 }
346done:
347 return ret;
348}
349
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600350/*
351 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
352 * @mmu - Pointer to mmu device
353 * data - Pointer to the platform data containing information about
354 * iommu devices for one iommu unit
355 * unit_id - The IOMMU unit number. This is not a specific ID but just
356 * a serial number. The serial numbers are treated as ID's of the
357 * IOMMU units
358 *
359 * Return - 0 on success else error code
360 */
361static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
362 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700363{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600364 struct kgsl_iommu *iommu = mmu->priv;
365 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700366 int i;
367
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600368 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
369 KGSL_CORE_ERR("Too many iommu devices defined for an "
370 "IOMMU unit\n");
371 return -EINVAL;
372 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700373
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600374 for (i = 0; i < data->iommu_ctx_count; i++) {
375 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700376 continue;
377
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600378 iommu_unit->dev[iommu_unit->dev_count].dev =
379 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
380 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
381 KGSL_CORE_ERR("Failed to get iommu dev handle for "
382 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700383 return -EINVAL;
384 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600385 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
386 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
387 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
388 data->iommu_ctxs[i].ctx_id);
389 return -EINVAL;
390 }
391 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
392 data->iommu_ctxs[i].ctx_id;
Jordan Crouse95b68472012-05-25 10:25:01 -0600393 iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
394
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600395 KGSL_DRV_INFO(mmu->device,
396 "Obtained dev handle %p for iommu context %s\n",
397 iommu_unit->dev[iommu_unit->dev_count].dev,
398 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700399
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600400 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700401 }
402
403 return 0;
404}
405
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600406/*
407 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
408 * @mmu - Pointer to mmu device
409 *
410 * Get the device pointers for the IOMMU user and priv contexts of the
411 * kgsl device
412 * Return - 0 on success else error code
413 */
414static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600415{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600416 struct platform_device *pdev =
417 container_of(mmu->device->parentdev, struct platform_device,
418 dev);
419 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
420 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700421 int i, ret = 0;
422
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600423 /* Go through the IOMMU data and get all the context devices */
424 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
425 KGSL_CORE_ERR("Too many IOMMU units defined\n");
426 ret = -EINVAL;
427 goto done;
428 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700429
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600430 for (i = 0; i < pdata_dev->iommu_count; i++) {
431 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700432 if (ret)
433 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600434 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600435 iommu->unit_count = pdata_dev->iommu_count;
436done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700437 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600438}
439
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600440/*
441 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
442 * of the respective iommu units
443 * @mmu - Pointer to mmu structure
444 *
445 * Return - 0 on success else error code
446 */
447static int kgsl_set_register_map(struct kgsl_mmu *mmu)
448{
449 struct platform_device *pdev =
450 container_of(mmu->device->parentdev, struct platform_device,
451 dev);
452 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
453 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
454 struct kgsl_iommu_unit *iommu_unit;
455 int i = 0, ret = 0;
456
457 for (; i < pdata_dev->iommu_count; i++) {
458 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
459 iommu_unit = &iommu->iommu_units[i];
460 /* set up the IOMMU register map for the given IOMMU unit */
461 if (!data.physstart || !data.physend) {
462 KGSL_CORE_ERR("The register range for IOMMU unit not"
463 " specified\n");
464 ret = -EINVAL;
465 goto err;
466 }
467 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
468 data.physend - data.physstart + 1);
469 if (!iommu_unit->reg_map.hostptr) {
470 KGSL_CORE_ERR("Failed to map SMMU register address "
471 "space from %x to %x\n", data.physstart,
472 data.physend - data.physstart + 1);
473 ret = -ENOMEM;
474 i--;
475 goto err;
476 }
477 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
478 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600479 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
480 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600481 }
482 iommu->unit_count = pdata_dev->iommu_count;
483 return ret;
484err:
485 /* Unmap any mapped IOMMU regions */
486 for (; i >= 0; i--) {
487 iommu_unit = &iommu->iommu_units[i];
488 iounmap(iommu_unit->reg_map.hostptr);
489 iommu_unit->reg_map.size = 0;
490 iommu_unit->reg_map.physaddr = 0;
491 }
492 return ret;
493}
494
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600495/*
496 * kgsl_iommu_pt_get_base_addr - Get the address of the pagetable that the
497 * IOMMU ttbr0 register is programmed with
498 * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
499 *
500 * Return - actual pagetable address that the ttbr0 register is programmed
501 * with
502 */
503static unsigned int kgsl_iommu_pt_get_base_addr(struct kgsl_pagetable *pt)
504{
505 struct kgsl_iommu_pt *iommu_pt = pt->priv;
506 return iommu_get_pt_base_addr(iommu_pt->domain);
507}
508
509/*
510 * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
511 * @mmu - Pointer to mmu structure
512 * @hostptr - Pointer to the IOMMU register map. This is used to match
513 * the iommu device whose lsb value is to be returned
514 * @ctx_id - The context bank whose lsb valus is to be returned
515 * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
516 * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
517 * are only programmed once in the beginning when a domain is attached
518 * does not change.
519 */
520static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
521 unsigned int unit_id,
522 enum kgsl_iommu_context_id ctx_id)
523{
524 struct kgsl_iommu *iommu = mmu->priv;
525 int i, j;
526 for (i = 0; i < iommu->unit_count; i++) {
527 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
528 for (j = 0; j < iommu_unit->dev_count; j++)
529 if (unit_id == i &&
530 ctx_id == iommu_unit->dev[j].ctx_id)
531 return iommu_unit->dev[j].pt_lsb;
532 }
533 return 0;
534}
535
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600536static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600537 struct kgsl_pagetable *pagetable)
538{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600539 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600540 struct kgsl_iommu *iommu = mmu->priv;
541 struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600542 /* page table not current, then setup mmu to use new
543 * specified page table
544 */
545 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600546 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600547 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600548 /* force tlb flush if asid is reused */
549 if (iommu->asid_reuse &&
550 (KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
551 flags |= KGSL_MMUFLAGS_TLBFLUSH;
552 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
553 mmu->device->id);
554 kgsl_setstate(mmu, KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600555 }
556 }
557}
558
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600559static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600560{
561 /*
562 * intialize device mmu
563 *
564 * call this with the global lock held
565 */
566 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600567 struct kgsl_iommu *iommu;
568
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600569 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
570 if (!iommu) {
571 KGSL_CORE_ERR("kzalloc(%d) failed\n",
572 sizeof(struct kgsl_iommu));
573 return -ENOMEM;
574 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600575 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
576 sizeof(unsigned long), GFP_KERNEL);
577 if (!iommu->asids) {
578 KGSL_CORE_ERR("kzalloc(%d) failed\n",
579 sizeof(struct kgsl_iommu));
580 status = -ENOMEM;
581 goto done;
582 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600583
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600584 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600585 status = kgsl_get_iommu_ctxt(mmu);
586 if (status)
587 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600588 status = kgsl_set_register_map(mmu);
589 if (status)
590 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600591
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600592 /* A nop is required in an indirect buffer when switching
593 * pagetables in-stream */
594 kgsl_sharedmem_writel(&mmu->setstate_memory,
595 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
596 cp_nop_packet(1));
597
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600598 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600599 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600600done:
601 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600602 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600603 kfree(iommu);
604 mmu->priv = NULL;
605 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600606 return status;
607}
608
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600609/*
610 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
611 * for iommu. This function is only called once during first start, successive
612 * start do not call this funciton.
613 * @mmu - Pointer to mmu structure
614 *
615 * Create the initial defaultpagetable and setup the iommu mappings to it
616 * Return - 0 on success else error code
617 */
618static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
619{
620 int status = 0;
621 int i = 0;
622 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600623 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600624 struct kgsl_pagetable *pagetable = NULL;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600625
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600626 /* If chip is not 8960 then we use the 2nd context bank for pagetable
627 * switching on the 3D side for which a separate table is allocated */
628 if (!cpu_is_msm8960()) {
629 mmu->priv_bank_table =
630 kgsl_mmu_getpagetable(KGSL_MMU_PRIV_BANK_TABLE_NAME);
631 if (mmu->priv_bank_table == NULL) {
632 status = -ENOMEM;
633 goto err;
634 }
635 iommu_pt = mmu->priv_bank_table->priv;
636 iommu_pt->asid = 1;
637 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600638 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
639 /* Return error if the default pagetable doesn't exist */
640 if (mmu->defaultpagetable == NULL) {
641 status = -ENOMEM;
642 goto err;
643 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600644 pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
645 mmu->defaultpagetable;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600646 /* Map the IOMMU regsiters to only defaultpagetable */
647 for (i = 0; i < iommu->unit_count; i++) {
648 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600649 status = kgsl_mmu_map(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600650 &(iommu->iommu_units[i].reg_map),
651 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
652 if (status) {
653 iommu->iommu_units[i].reg_map.priv &=
654 ~KGSL_MEMFLAGS_GLOBAL;
655 goto err;
656 }
657 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600658 /*
659 * The dafault pagetable always has asid 0 assigned by the iommu driver
660 * and asid 1 is assigned to the private context.
661 */
662 iommu_pt = mmu->defaultpagetable->priv;
663 iommu_pt->asid = 0;
664 set_bit(0, iommu->asids);
665 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600666 return status;
667err:
668 for (i--; i >= 0; i--) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600669 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600670 &(iommu->iommu_units[i].reg_map));
671 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
672 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600673 if (mmu->priv_bank_table) {
674 kgsl_mmu_putpagetable(mmu->priv_bank_table);
675 mmu->priv_bank_table = NULL;
676 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600677 if (mmu->defaultpagetable) {
678 kgsl_mmu_putpagetable(mmu->defaultpagetable);
679 mmu->defaultpagetable = NULL;
680 }
681 return status;
682}
683
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600684static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600685{
686 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600687 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600688 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600689
690 if (mmu->flags & KGSL_FLAGS_STARTED)
691 return 0;
692
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600693 if (mmu->defaultpagetable == NULL) {
694 status = kgsl_iommu_setup_defaultpagetable(mmu);
695 if (status)
696 return -ENOMEM;
697 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600698 /* We use the GPU MMU to control access to IOMMU registers on 8960 with
699 * a225, hence we still keep the MMU active on 8960 */
700 if (cpu_is_msm8960()) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600701 struct kgsl_mh *mh = &(mmu->device->mh);
702 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
703 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
704 mh->mpu_base +
Shubhraprakash Dasc6e21012012-05-11 17:24:51 -0600705 iommu->iommu_units
706 [iommu->unit_count - 1].reg_map.gpuaddr -
707 PAGE_SIZE);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600708 } else {
709 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
710 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600711
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600712 mmu->hwpagetable = mmu->defaultpagetable;
713
714 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600715 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600716 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600717 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600718 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600719 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600720 if (status) {
721 KGSL_CORE_ERR("clk enable failed\n");
722 goto done;
723 }
724 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
725 if (status) {
726 KGSL_CORE_ERR("clk enable failed\n");
727 goto done;
728 }
729 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
730 * that value should not change when we change pagetables, so while
731 * changing pagetables we can use this lsb value of the pagetable w/o
732 * having to read it again
733 */
734 for (i = 0; i < iommu->unit_count; i++) {
735 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600736 /* Make sure that the ASID of the priv bank is set to 1.
737 * When we a different pagetable for the priv bank then the
738 * iommu driver sets the ASID to 0 instead of 1 */
739 KGSL_IOMMU_SET_IOMMU_REG(iommu->iommu_units[i].reg_map.hostptr,
740 KGSL_IOMMU_CONTEXT_PRIV,
741 CONTEXTIDR, 1);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600742 for (j = 0; j < iommu_unit->dev_count; j++)
743 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
744 KGSL_IOMMU_GET_IOMMU_REG(
745 iommu_unit->reg_map.hostptr,
746 iommu_unit->dev[j].ctx_id,
747 TTBR0));
748 }
749 iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
750 iommu->iommu_units[0].reg_map.hostptr,
751 KGSL_IOMMU_CONTEXT_USER,
752 CONTEXTIDR);
753
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600754 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600755 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600756
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600757done:
758 if (status) {
759 kgsl_iommu_disable_clk(mmu);
760 kgsl_detach_pagetable_iommu_domain(mmu);
761 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600762 return status;
763}
764
765static int
766kgsl_iommu_unmap(void *mmu_specific_pt,
767 struct kgsl_memdesc *memdesc)
768{
769 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600770 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600771 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600772
773 /* All GPU addresses as assigned are page aligned, but some
774 functions purturb the gpuaddr with an offset, so apply the
775 mask here to make sure we have the right address */
776
777 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
778
779 if (range == 0 || gpuaddr == 0)
780 return 0;
781
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600782 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600783 if (ret)
784 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600785 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600786 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600787
788 return 0;
789}
790
791static int
792kgsl_iommu_map(void *mmu_specific_pt,
793 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600794 unsigned int protflags,
795 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600796{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600797 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600798 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600799 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600800 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600801
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600802 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600803
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600804
Jordan Croused17e9aa2011-10-12 16:57:48 -0600805 iommu_virt_addr = memdesc->gpuaddr;
806
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600807 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600808 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600809 if (ret) {
810 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600811 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600812 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600813 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600814 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600815 }
816
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600817#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
818 /*
819 * Flushing only required if per process pagetables are used. With
820 * global case, flushing will happen inside iommu_map function
821 */
822 if (!ret)
823 *tlb_flags = UINT_MAX;
824#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600825 return ret;
826}
827
Shubhraprakash Das79447952012-04-26 18:12:23 -0600828static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600829{
830 /*
831 * stop device mmu
832 *
833 * call this with the global lock held
834 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600835
836 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600837 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600838 /* detach iommu attachment */
839 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600840 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600841
842 mmu->flags &= ~KGSL_FLAGS_STARTED;
843 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600844}
845
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600846static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600847{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600848 struct kgsl_iommu *iommu = mmu->priv;
849 int i;
850 for (i = 0; i < iommu->unit_count; i++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600851 struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
852 mmu->priv_bank_table : mmu->defaultpagetable);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600853 if (iommu->iommu_units[i].reg_map.gpuaddr)
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600854 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600855 &(iommu->iommu_units[i].reg_map));
856 if (iommu->iommu_units[i].reg_map.hostptr)
857 iounmap(iommu->iommu_units[i].reg_map.hostptr);
858 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
859 iommu->iommu_units[i].reg_map.sglen);
860 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600861
862 if (mmu->priv_bank_table)
863 kgsl_mmu_putpagetable(mmu->priv_bank_table);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600864 if (mmu->defaultpagetable)
865 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600866 kfree(iommu->asids);
867 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600868
869 return 0;
870}
871
872static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600873kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600874{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600875 unsigned int pt_base;
876 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das64a745c2012-06-11 16:44:16 -0600877 /* We cannot enable or disable the clocks in interrupt context, this
878 function is called from interrupt context if there is an axi error */
879 if (in_interrupt())
880 return 0;
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600881 /* Return the current pt base by reading IOMMU pt_base register */
882 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
883 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
884 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
885 KGSL_IOMMU_TTBR0);
886 kgsl_iommu_disable_clk(mmu);
887 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
888 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600889}
890
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600891/*
892 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
893 * pagetable
894 * @mmu - Pointer to mmu structure
895 *
896 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
897 * are unique identifiers for pagetable that can be used to selectively flush
898 * tlb entries of the IOMMU unit.
899 * Return - asid to be used with the IOMMU domain
900 */
901static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
902{
903 struct kgsl_iommu *iommu = mmu->priv;
904 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
905
906 /*
907 * If the iommu pagetable does not have any asid assigned and is not the
908 * default pagetable then assign asid.
909 */
910 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
911 iommu_pt->asid = find_first_zero_bit(iommu->asids,
912 KGSL_IOMMU_MAX_ASIDS);
913 /* No free bits means reuse asid */
914 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
915 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
916 iommu->asid_reuse++;
917 }
918 set_bit(iommu_pt->asid, iommu->asids);
919 /*
920 * Store pointer to asids list so that during pagetable destroy
921 * the asid assigned to this pagetable may be cleared
922 */
923 iommu_pt->iommu = iommu;
924 }
925 /* Return the asid + the constant part of asid that never changes */
926 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
927 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
928 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
929 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
930}
931
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600932/*
933 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
934 * of the primary context bank
935 * @mmu - Pointer to mmu structure
936 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
937 * flushed or both
938 *
939 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
940 * do both by doing direct register writes to the IOMMu registers through the
941 * cpu
942 * Return - void
943 */
944static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
945 uint32_t flags)
946{
947 struct kgsl_iommu *iommu = mmu->priv;
948 int temp;
949 int i;
950 unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
951 mmu->hwpagetable);
952 unsigned int pt_val;
953
954 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
955 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
956 return;
957 }
958 /* Mask off the lsb of the pt base address since lsb will not change */
959 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
960 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
961 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
962 for (i = 0; i < iommu->unit_count; i++) {
963 /* get the lsb value which should not change when
964 * changing ttbr0 */
965 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
966 KGSL_IOMMU_CONTEXT_USER);
967 pt_val += pt_base;
968
969 KGSL_IOMMU_SET_IOMMU_REG(
970 iommu->iommu_units[i].reg_map.hostptr,
971 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
972
973 mb();
974 temp = KGSL_IOMMU_GET_IOMMU_REG(
975 iommu->iommu_units[i].reg_map.hostptr,
976 KGSL_IOMMU_CONTEXT_USER, TTBR0);
977 /* Set asid */
978 KGSL_IOMMU_SET_IOMMU_REG(
979 iommu->iommu_units[i].reg_map.hostptr,
980 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
981 kgsl_iommu_get_hwpagetable_asid(mmu));
982 mb();
983 temp = KGSL_IOMMU_GET_IOMMU_REG(
984 iommu->iommu_units[i].reg_map.hostptr,
985 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
986 }
987 }
988 /* Flush tlb */
989 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
990 for (i = 0; i < iommu->unit_count; i++) {
991 KGSL_IOMMU_SET_IOMMU_REG(
992 iommu->iommu_units[i].reg_map.hostptr,
993 KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
994 kgsl_iommu_get_hwpagetable_asid(mmu));
995 mb();
996 }
997 }
998 /* Disable smmu clock */
999 kgsl_iommu_disable_clk(mmu);
1000}
1001
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001002/*
1003 * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
1004 * the address of memory descriptors which map the IOMMU registers
1005 * @mmu - Pointer to mmu structure
1006 * @reg_map_desc - Out parameter in which the address of the array containing
1007 * pointers to register map descriptors is returned. The caller is supposed
1008 * to free this array
1009 *
1010 * Return - The number of iommu units which is also the number of register
1011 * mapped descriptor arrays which the out parameter will have
1012 */
1013static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
1014 void **reg_map_desc)
1015{
1016 struct kgsl_iommu *iommu = mmu->priv;
1017 void **reg_desc_ptr;
1018 int i;
1019
1020 /*
1021 * Alocate array of pointers that will hold address of the register map
1022 * descriptors
1023 */
1024 reg_desc_ptr = kmalloc(iommu->unit_count *
1025 sizeof(struct kgsl_memdesc *), GFP_KERNEL);
1026 if (!reg_desc_ptr) {
1027 KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
1028 iommu->unit_count * sizeof(struct kgsl_memdesc *));
1029 return -ENOMEM;
1030 }
1031
1032 for (i = 0; i < iommu->unit_count; i++)
1033 reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
1034
1035 *reg_map_desc = reg_desc_ptr;
1036 return i;
1037}
1038
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001039struct kgsl_mmu_ops iommu_ops = {
1040 .mmu_init = kgsl_iommu_init,
1041 .mmu_close = kgsl_iommu_close,
1042 .mmu_start = kgsl_iommu_start,
1043 .mmu_stop = kgsl_iommu_stop,
1044 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001045 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001046 .mmu_pagefault = NULL,
1047 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001048 .mmu_enable_clk = kgsl_iommu_enable_clk,
1049 .mmu_disable_clk = kgsl_iommu_disable_clk,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001050 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001051 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001052 .mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001053};
1054
1055struct kgsl_mmu_pt_ops iommu_pt_ops = {
1056 .mmu_map = kgsl_iommu_map,
1057 .mmu_unmap = kgsl_iommu_unmap,
1058 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1059 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
1060 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das5a610b52012-05-09 17:31:54 -06001061 .mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001062};