blob: 8d66eaa214814b3e1f5b7158659928d2670d4a32 [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/*
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600133 * kgsl_iommu_disable_clk_event - An event function that is executed when
134 * the required timestamp is reached. It disables the IOMMU clocks if
135 * the timestamp on which the clocks can be disabled has expired.
136 * @device - The kgsl device pointer
137 * @data - The data passed during event creation, it is the MMU pointer
138 * @id - Context ID, should always be KGSL_MEMSTORE_GLOBAL
139 * @ts - The current timestamp that has expired for the device
140 *
141 * Disables IOMMU clocks if timestamp has expired
142 * Return - void
143 */
144static void kgsl_iommu_clk_disable_event(struct kgsl_device *device, void *data,
145 unsigned int id, unsigned int ts)
146{
147 struct kgsl_mmu *mmu = data;
148 struct kgsl_iommu *iommu = mmu->priv;
149
150 if (!iommu->clk_event_queued) {
151 if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts))
152 KGSL_DRV_ERR(device,
153 "IOMMU disable clock event being cancelled, "
154 "iommu_last_cmd_ts: %x, retired ts: %x\n",
155 iommu->iommu_last_cmd_ts, ts);
156 return;
157 }
158
159 if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) {
160 kgsl_iommu_disable_clk(mmu);
161 iommu->clk_event_queued = false;
162 } else {
163 /* add new event to fire when ts is reached, this can happen
164 * if we queued an event and someone requested the clocks to
165 * be disbaled on a later timestamp */
166 if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts,
167 kgsl_iommu_clk_disable_event, mmu, mmu)) {
168 KGSL_DRV_ERR(device,
169 "Failed to add IOMMU disable clk event\n");
170 iommu->clk_event_queued = false;
171 }
172 }
173}
174
175/*
176 * kgsl_iommu_disable_clk_on_ts - Sets up event to disable IOMMU clocks
177 * @mmu - The kgsl MMU pointer
178 * @ts - Timestamp on which the clocks should be disabled
179 * @ts_valid - Indicates whether ts parameter is valid, if this parameter
180 * is false then it means that the calling function wants to disable the
181 * IOMMU clocks immediately without waiting for any timestamp
182 *
183 * Creates an event to disable the IOMMU clocks on timestamp and if event
184 * already exists then updates the timestamp of disabling the IOMMU clocks
185 * with the passed in ts if it is greater than the current value at which
186 * the clocks will be disabled
187 * Return - void
188 */
189static void
190kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu, unsigned int ts,
191 bool ts_valid)
192{
193 struct kgsl_iommu *iommu = mmu->priv;
194
195 if (iommu->clk_event_queued) {
196 if (ts_valid && (0 <
197 timestamp_cmp(ts, iommu->iommu_last_cmd_ts)))
198 iommu->iommu_last_cmd_ts = ts;
199 } else {
200 if (ts_valid) {
201 iommu->iommu_last_cmd_ts = ts;
202 iommu->clk_event_queued = true;
203 if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
204 ts, kgsl_iommu_clk_disable_event, mmu, mmu)) {
205 KGSL_DRV_ERR(mmu->device,
206 "Failed to add IOMMU disable clk event\n");
207 iommu->clk_event_queued = false;
208 }
209 } else {
210 kgsl_iommu_disable_clk(mmu);
211 }
212 }
213}
214
215/*
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600216 * kgsl_iommu_enable_clk - Enable iommu clocks
217 * @mmu - Pointer to mmu structure
218 * @ctx_id - The context bank whose clocks are to be turned on
219 *
220 * Enables iommu clocks of a given context
221 * Return: 0 on success else error code
222 */
223static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
224 int ctx_id)
225{
226 int ret = 0;
227 int i, j;
228 struct kgsl_iommu *iommu = mmu->priv;
229 struct msm_iommu_drvdata *iommu_drvdata;
230
231 for (i = 0; i < iommu->unit_count; i++) {
232 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
233 for (j = 0; j < iommu_unit->dev_count; j++) {
234 if (iommu_unit->dev[j].clk_enabled ||
235 ctx_id != iommu_unit->dev[j].ctx_id)
236 continue;
237 iommu_drvdata =
238 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
239 ret = clk_prepare_enable(iommu_drvdata->pclk);
240 if (ret)
241 goto done;
242 if (iommu_drvdata->clk) {
243 ret = clk_prepare_enable(iommu_drvdata->clk);
244 if (ret) {
245 clk_disable_unprepare(
246 iommu_drvdata->pclk);
247 goto done;
248 }
249 }
250 iommu_unit->dev[j].clk_enabled = true;
251 }
252 }
253done:
254 if (ret)
255 kgsl_iommu_disable_clk(mmu);
256 return ret;
257}
258
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600259/*
260 * kgsl_iommu_pt_equal - Check if pagetables are equal
261 * @pt - Pointer to pagetable
262 * @pt_base - Address of a pagetable that the IOMMU register is
263 * programmed with
264 *
265 * Checks whether the pt_base is equal to the base address of
266 * the pagetable which is contained in the pt structure
267 * Return - Non-zero if the pagetable addresses are equal else 0
268 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600269static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
270 unsigned int pt_base)
271{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600272 struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
273 unsigned int domain_ptbase = iommu_pt ?
274 iommu_get_pt_base_addr(iommu_pt->domain) : 0;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600275 /* Only compare the valid address bits of the pt_base */
276 domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
277 KGSL_IOMMU_TTBR0_PA_SHIFT);
278 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
279 KGSL_IOMMU_TTBR0_PA_SHIFT);
280 return domain_ptbase && pt_base &&
281 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600282}
283
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600284/*
285 * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
286 * @mmu_specific_pt - Pointer to pagetable which is to be freed
287 *
288 * Return - void
289 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600290static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
291{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600292 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
293 if (iommu_pt->domain)
294 iommu_domain_free(iommu_pt->domain);
295 if (iommu_pt->iommu) {
296 if ((KGSL_IOMMU_ASID_REUSE == iommu_pt->asid) &&
297 iommu_pt->iommu->asid_reuse)
298 iommu_pt->iommu->asid_reuse--;
299 if (!iommu_pt->iommu->asid_reuse ||
300 (KGSL_IOMMU_ASID_REUSE != iommu_pt->asid))
301 clear_bit(iommu_pt->asid, iommu_pt->iommu->asids);
302 }
303 kfree(iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600304}
305
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600306/*
307 * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
308 *
309 * Allocate memory to hold a pagetable and allocate the IOMMU
310 * domain which is the actual IOMMU pagetable
311 * Return - void
312 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600313void *kgsl_iommu_create_pagetable(void)
314{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600315 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600316
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600317 iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
318 if (!iommu_pt) {
319 KGSL_CORE_ERR("kzalloc(%d) failed\n",
320 sizeof(struct kgsl_iommu_pt));
321 return NULL;
322 }
Steve Mucklef132c6c2012-06-06 18:30:57 -0700323 iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
324 MSM_IOMMU_DOMAIN_PT_CACHEABLE);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600325 if (!iommu_pt->domain) {
326 KGSL_CORE_ERR("Failed to create iommu domain\n");
327 kfree(iommu_pt);
328 return NULL;
Jordan Crouse95b68472012-05-25 10:25:01 -0600329 } else {
330 iommu_set_fault_handler(iommu_pt->domain,
331 kgsl_iommu_fault_handler);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600332 }
Jordan Crouse95b68472012-05-25 10:25:01 -0600333
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600334 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600335}
336
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600337/*
338 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
339 * pagetable
340 * @mmu - Pointer to the device mmu structure
341 * @priv - Flag indicating whether the private or user context is to be
342 * detached
343 *
344 * Detach the IOMMU unit with the domain that is contained in the
345 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
346 * in use because the PTBR will not be set after a detach
347 * Return - void
348 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600349static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
350{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600351 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600352 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600353 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600354
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600355 for (i = 0; i < iommu->unit_count; i++) {
356 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600357 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600358 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600359 /*
360 * If there is a 2nd default pagetable then priv domain
361 * is attached with this pagetable
362 */
363 if (mmu->priv_bank_table &&
364 (KGSL_IOMMU_CONTEXT_PRIV == j))
365 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600366 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600367 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600368 iommu_unit->dev[j].dev);
369 iommu_unit->dev[j].attached = false;
370 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
371 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600372 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600373 }
374 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600375 }
376}
377
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600378/*
379 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
380 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
381 * setup other IOMMU registers for the device so that it becomes
382 * active
383 * @mmu - Pointer to the device mmu structure
384 * @priv - Flag indicating whether the private or user context is to be
385 * attached
386 *
387 * Attach the IOMMU unit with the domain that is contained in the
388 * hwpagetable of the given mmu.
389 * Return - 0 on success else error code
390 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600391static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
392{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600393 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600394 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600395 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600396
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600397 /*
398 * Loop through all the iommu devcies under all iommu units and
399 * attach the domain
400 */
401 for (i = 0; i < iommu->unit_count; i++) {
402 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600403 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600404 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600405 /*
406 * If there is a 2nd default pagetable then priv domain
407 * is attached to this pagetable
408 */
409 if (mmu->priv_bank_table &&
410 (KGSL_IOMMU_CONTEXT_PRIV == j))
411 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600412 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600413 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600414 iommu_unit->dev[j].dev);
415 if (ret) {
416 KGSL_MEM_ERR(mmu->device,
417 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700418 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600419 goto done;
420 }
421 iommu_unit->dev[j].attached = true;
422 KGSL_MEM_INFO(mmu->device,
423 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600424 iommu_pt->domain, iommu_unit->dev[j].dev,
425 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700426 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600427 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600428 }
429done:
430 return ret;
431}
432
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600433/*
434 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
435 * @mmu - Pointer to mmu device
436 * data - Pointer to the platform data containing information about
437 * iommu devices for one iommu unit
438 * unit_id - The IOMMU unit number. This is not a specific ID but just
439 * a serial number. The serial numbers are treated as ID's of the
440 * IOMMU units
441 *
442 * Return - 0 on success else error code
443 */
444static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
445 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700446{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600447 struct kgsl_iommu *iommu = mmu->priv;
448 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700449 int i;
450
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600451 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
452 KGSL_CORE_ERR("Too many iommu devices defined for an "
453 "IOMMU unit\n");
454 return -EINVAL;
455 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700456
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600457 for (i = 0; i < data->iommu_ctx_count; i++) {
458 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700459 continue;
460
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600461 iommu_unit->dev[iommu_unit->dev_count].dev =
462 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
463 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
464 KGSL_CORE_ERR("Failed to get iommu dev handle for "
465 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700466 return -EINVAL;
467 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600468 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
469 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
470 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
471 data->iommu_ctxs[i].ctx_id);
472 return -EINVAL;
473 }
474 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
475 data->iommu_ctxs[i].ctx_id;
Jordan Crouse95b68472012-05-25 10:25:01 -0600476 iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
477
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600478 KGSL_DRV_INFO(mmu->device,
479 "Obtained dev handle %p for iommu context %s\n",
480 iommu_unit->dev[iommu_unit->dev_count].dev,
481 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700482
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600483 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700484 }
485
486 return 0;
487}
488
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600489/*
490 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
491 * @mmu - Pointer to mmu device
492 *
493 * Get the device pointers for the IOMMU user and priv contexts of the
494 * kgsl device
495 * Return - 0 on success else error code
496 */
497static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600498{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600499 struct platform_device *pdev =
500 container_of(mmu->device->parentdev, struct platform_device,
501 dev);
502 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
503 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700504 int i, ret = 0;
505
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600506 /* Go through the IOMMU data and get all the context devices */
507 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
508 KGSL_CORE_ERR("Too many IOMMU units defined\n");
509 ret = -EINVAL;
510 goto done;
511 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700512
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600513 for (i = 0; i < pdata_dev->iommu_count; i++) {
514 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700515 if (ret)
516 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600517 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600518 iommu->unit_count = pdata_dev->iommu_count;
519done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700520 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600521}
522
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600523/*
524 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
525 * of the respective iommu units
526 * @mmu - Pointer to mmu structure
527 *
528 * Return - 0 on success else error code
529 */
530static int kgsl_set_register_map(struct kgsl_mmu *mmu)
531{
532 struct platform_device *pdev =
533 container_of(mmu->device->parentdev, struct platform_device,
534 dev);
535 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
536 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
537 struct kgsl_iommu_unit *iommu_unit;
538 int i = 0, ret = 0;
539
540 for (; i < pdata_dev->iommu_count; i++) {
541 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
542 iommu_unit = &iommu->iommu_units[i];
543 /* set up the IOMMU register map for the given IOMMU unit */
544 if (!data.physstart || !data.physend) {
545 KGSL_CORE_ERR("The register range for IOMMU unit not"
546 " specified\n");
547 ret = -EINVAL;
548 goto err;
549 }
550 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
551 data.physend - data.physstart + 1);
552 if (!iommu_unit->reg_map.hostptr) {
553 KGSL_CORE_ERR("Failed to map SMMU register address "
554 "space from %x to %x\n", data.physstart,
555 data.physend - data.physstart + 1);
556 ret = -ENOMEM;
557 i--;
558 goto err;
559 }
560 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
561 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600562 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
563 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600564 }
565 iommu->unit_count = pdata_dev->iommu_count;
566 return ret;
567err:
568 /* Unmap any mapped IOMMU regions */
569 for (; i >= 0; i--) {
570 iommu_unit = &iommu->iommu_units[i];
571 iounmap(iommu_unit->reg_map.hostptr);
572 iommu_unit->reg_map.size = 0;
573 iommu_unit->reg_map.physaddr = 0;
574 }
575 return ret;
576}
577
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600578/*
579 * kgsl_iommu_pt_get_base_addr - Get the address of the pagetable that the
580 * IOMMU ttbr0 register is programmed with
581 * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
582 *
583 * Return - actual pagetable address that the ttbr0 register is programmed
584 * with
585 */
586static unsigned int kgsl_iommu_pt_get_base_addr(struct kgsl_pagetable *pt)
587{
588 struct kgsl_iommu_pt *iommu_pt = pt->priv;
589 return iommu_get_pt_base_addr(iommu_pt->domain);
590}
591
592/*
593 * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
594 * @mmu - Pointer to mmu structure
595 * @hostptr - Pointer to the IOMMU register map. This is used to match
596 * the iommu device whose lsb value is to be returned
597 * @ctx_id - The context bank whose lsb valus is to be returned
598 * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
599 * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
600 * are only programmed once in the beginning when a domain is attached
601 * does not change.
602 */
603static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
604 unsigned int unit_id,
605 enum kgsl_iommu_context_id ctx_id)
606{
607 struct kgsl_iommu *iommu = mmu->priv;
608 int i, j;
609 for (i = 0; i < iommu->unit_count; i++) {
610 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
611 for (j = 0; j < iommu_unit->dev_count; j++)
612 if (unit_id == i &&
613 ctx_id == iommu_unit->dev[j].ctx_id)
614 return iommu_unit->dev[j].pt_lsb;
615 }
616 return 0;
617}
618
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600619static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600620 struct kgsl_pagetable *pagetable)
621{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600622 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600623 struct kgsl_iommu *iommu = mmu->priv;
624 struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600625 /* page table not current, then setup mmu to use new
626 * specified page table
627 */
628 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600629 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600630 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600631 /* force tlb flush if asid is reused */
632 if (iommu->asid_reuse &&
633 (KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
634 flags |= KGSL_MMUFLAGS_TLBFLUSH;
635 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
636 mmu->device->id);
637 kgsl_setstate(mmu, KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600638 }
639 }
640}
641
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600642static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600643{
644 /*
645 * intialize device mmu
646 *
647 * call this with the global lock held
648 */
649 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600650 struct kgsl_iommu *iommu;
651
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600652 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
653 if (!iommu) {
654 KGSL_CORE_ERR("kzalloc(%d) failed\n",
655 sizeof(struct kgsl_iommu));
656 return -ENOMEM;
657 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600658 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
659 sizeof(unsigned long), GFP_KERNEL);
660 if (!iommu->asids) {
661 KGSL_CORE_ERR("kzalloc(%d) failed\n",
662 sizeof(struct kgsl_iommu));
663 status = -ENOMEM;
664 goto done;
665 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600666
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600667 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600668 status = kgsl_get_iommu_ctxt(mmu);
669 if (status)
670 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600671 status = kgsl_set_register_map(mmu);
672 if (status)
673 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600674
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600675 /* A nop is required in an indirect buffer when switching
676 * pagetables in-stream */
677 kgsl_sharedmem_writel(&mmu->setstate_memory,
678 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
679 cp_nop_packet(1));
680
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600681 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600682 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600683done:
684 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600685 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600686 kfree(iommu);
687 mmu->priv = NULL;
688 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600689 return status;
690}
691
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600692/*
693 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
694 * for iommu. This function is only called once during first start, successive
695 * start do not call this funciton.
696 * @mmu - Pointer to mmu structure
697 *
698 * Create the initial defaultpagetable and setup the iommu mappings to it
699 * Return - 0 on success else error code
700 */
701static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
702{
703 int status = 0;
704 int i = 0;
705 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600706 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600707 struct kgsl_pagetable *pagetable = NULL;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600708
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600709 /* If chip is not 8960 then we use the 2nd context bank for pagetable
710 * switching on the 3D side for which a separate table is allocated */
711 if (!cpu_is_msm8960()) {
712 mmu->priv_bank_table =
713 kgsl_mmu_getpagetable(KGSL_MMU_PRIV_BANK_TABLE_NAME);
714 if (mmu->priv_bank_table == NULL) {
715 status = -ENOMEM;
716 goto err;
717 }
718 iommu_pt = mmu->priv_bank_table->priv;
719 iommu_pt->asid = 1;
720 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600721 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
722 /* Return error if the default pagetable doesn't exist */
723 if (mmu->defaultpagetable == NULL) {
724 status = -ENOMEM;
725 goto err;
726 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600727 pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
728 mmu->defaultpagetable;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600729 /* Map the IOMMU regsiters to only defaultpagetable */
730 for (i = 0; i < iommu->unit_count; i++) {
731 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600732 status = kgsl_mmu_map(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600733 &(iommu->iommu_units[i].reg_map),
734 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
735 if (status) {
736 iommu->iommu_units[i].reg_map.priv &=
737 ~KGSL_MEMFLAGS_GLOBAL;
738 goto err;
739 }
740 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600741 /*
742 * The dafault pagetable always has asid 0 assigned by the iommu driver
743 * and asid 1 is assigned to the private context.
744 */
745 iommu_pt = mmu->defaultpagetable->priv;
746 iommu_pt->asid = 0;
747 set_bit(0, iommu->asids);
748 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600749 return status;
750err:
751 for (i--; i >= 0; i--) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600752 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600753 &(iommu->iommu_units[i].reg_map));
754 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
755 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600756 if (mmu->priv_bank_table) {
757 kgsl_mmu_putpagetable(mmu->priv_bank_table);
758 mmu->priv_bank_table = NULL;
759 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600760 if (mmu->defaultpagetable) {
761 kgsl_mmu_putpagetable(mmu->defaultpagetable);
762 mmu->defaultpagetable = NULL;
763 }
764 return status;
765}
766
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600767static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600768{
769 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600770 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600771 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600772
773 if (mmu->flags & KGSL_FLAGS_STARTED)
774 return 0;
775
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600776 if (mmu->defaultpagetable == NULL) {
777 status = kgsl_iommu_setup_defaultpagetable(mmu);
778 if (status)
779 return -ENOMEM;
780 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600781 /* We use the GPU MMU to control access to IOMMU registers on 8960 with
782 * a225, hence we still keep the MMU active on 8960 */
783 if (cpu_is_msm8960()) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600784 struct kgsl_mh *mh = &(mmu->device->mh);
785 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
786 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
787 mh->mpu_base +
Shubhraprakash Dasc6e21012012-05-11 17:24:51 -0600788 iommu->iommu_units
789 [iommu->unit_count - 1].reg_map.gpuaddr -
790 PAGE_SIZE);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600791 } else {
792 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
793 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600794
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600795 mmu->hwpagetable = mmu->defaultpagetable;
796
797 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600798 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600799 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600800 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600801 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600802 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600803 if (status) {
804 KGSL_CORE_ERR("clk enable failed\n");
805 goto done;
806 }
807 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
808 if (status) {
809 KGSL_CORE_ERR("clk enable failed\n");
810 goto done;
811 }
812 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
813 * that value should not change when we change pagetables, so while
814 * changing pagetables we can use this lsb value of the pagetable w/o
815 * having to read it again
816 */
817 for (i = 0; i < iommu->unit_count; i++) {
818 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600819 /* Make sure that the ASID of the priv bank is set to 1.
820 * When we a different pagetable for the priv bank then the
821 * iommu driver sets the ASID to 0 instead of 1 */
822 KGSL_IOMMU_SET_IOMMU_REG(iommu->iommu_units[i].reg_map.hostptr,
823 KGSL_IOMMU_CONTEXT_PRIV,
824 CONTEXTIDR, 1);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600825 for (j = 0; j < iommu_unit->dev_count; j++)
826 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
827 KGSL_IOMMU_GET_IOMMU_REG(
828 iommu_unit->reg_map.hostptr,
829 iommu_unit->dev[j].ctx_id,
830 TTBR0));
831 }
832 iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
833 iommu->iommu_units[0].reg_map.hostptr,
834 KGSL_IOMMU_CONTEXT_USER,
835 CONTEXTIDR);
836
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600837 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600838 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600839
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600840done:
841 if (status) {
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600842 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600843 kgsl_detach_pagetable_iommu_domain(mmu);
844 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600845 return status;
846}
847
848static int
849kgsl_iommu_unmap(void *mmu_specific_pt,
850 struct kgsl_memdesc *memdesc)
851{
852 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600853 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600854 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600855
856 /* All GPU addresses as assigned are page aligned, but some
857 functions purturb the gpuaddr with an offset, so apply the
858 mask here to make sure we have the right address */
859
860 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
861
862 if (range == 0 || gpuaddr == 0)
863 return 0;
864
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600865 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600866 if (ret)
867 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600868 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600869 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600870
871 return 0;
872}
873
874static int
875kgsl_iommu_map(void *mmu_specific_pt,
876 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600877 unsigned int protflags,
878 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600879{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600880 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600881 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600882 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600883 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600884
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600885 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600886
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600887
Jordan Croused17e9aa2011-10-12 16:57:48 -0600888 iommu_virt_addr = memdesc->gpuaddr;
889
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600890 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600891 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600892 if (ret) {
893 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600894 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600895 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600896 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600897 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600898 }
899
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600900#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
901 /*
902 * Flushing only required if per process pagetables are used. With
903 * global case, flushing will happen inside iommu_map function
904 */
905 if (!ret)
906 *tlb_flags = UINT_MAX;
907#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600908 return ret;
909}
910
Shubhraprakash Das79447952012-04-26 18:12:23 -0600911static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600912{
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600913 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600914 /*
915 * stop device mmu
916 *
917 * call this with the global lock held
918 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600919
920 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600921 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600922 /* detach iommu attachment */
923 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600924 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600925
926 mmu->flags &= ~KGSL_FLAGS_STARTED;
927 }
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600928
929 /* switch off MMU clocks and cancel any events it has queued */
930 iommu->clk_event_queued = false;
931 kgsl_cancel_events(mmu->device, mmu);
932 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600933}
934
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600935static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600936{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600937 struct kgsl_iommu *iommu = mmu->priv;
938 int i;
939 for (i = 0; i < iommu->unit_count; i++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600940 struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
941 mmu->priv_bank_table : mmu->defaultpagetable);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600942 if (iommu->iommu_units[i].reg_map.gpuaddr)
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600943 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600944 &(iommu->iommu_units[i].reg_map));
945 if (iommu->iommu_units[i].reg_map.hostptr)
946 iounmap(iommu->iommu_units[i].reg_map.hostptr);
947 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
948 iommu->iommu_units[i].reg_map.sglen);
949 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600950
951 if (mmu->priv_bank_table)
952 kgsl_mmu_putpagetable(mmu->priv_bank_table);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600953 if (mmu->defaultpagetable)
954 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600955 kfree(iommu->asids);
956 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600957
958 return 0;
959}
960
961static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600962kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600963{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600964 unsigned int pt_base;
965 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das64a745c2012-06-11 16:44:16 -0600966 /* We cannot enable or disable the clocks in interrupt context, this
967 function is called from interrupt context if there is an axi error */
968 if (in_interrupt())
969 return 0;
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600970 /* Return the current pt base by reading IOMMU pt_base register */
971 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
972 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
973 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
974 KGSL_IOMMU_TTBR0);
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600975 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600976 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
977 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600978}
979
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600980/*
981 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
982 * pagetable
983 * @mmu - Pointer to mmu structure
984 *
985 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
986 * are unique identifiers for pagetable that can be used to selectively flush
987 * tlb entries of the IOMMU unit.
988 * Return - asid to be used with the IOMMU domain
989 */
990static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
991{
992 struct kgsl_iommu *iommu = mmu->priv;
993 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
994
995 /*
996 * If the iommu pagetable does not have any asid assigned and is not the
997 * default pagetable then assign asid.
998 */
999 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
1000 iommu_pt->asid = find_first_zero_bit(iommu->asids,
1001 KGSL_IOMMU_MAX_ASIDS);
1002 /* No free bits means reuse asid */
1003 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
1004 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
1005 iommu->asid_reuse++;
1006 }
1007 set_bit(iommu_pt->asid, iommu->asids);
1008 /*
1009 * Store pointer to asids list so that during pagetable destroy
1010 * the asid assigned to this pagetable may be cleared
1011 */
1012 iommu_pt->iommu = iommu;
1013 }
1014 /* Return the asid + the constant part of asid that never changes */
1015 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
1016 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
1017 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
1018 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
1019}
1020
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001021/*
1022 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
1023 * of the primary context bank
1024 * @mmu - Pointer to mmu structure
1025 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
1026 * flushed or both
1027 *
1028 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
1029 * do both by doing direct register writes to the IOMMu registers through the
1030 * cpu
1031 * Return - void
1032 */
1033static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
1034 uint32_t flags)
1035{
1036 struct kgsl_iommu *iommu = mmu->priv;
1037 int temp;
1038 int i;
1039 unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
1040 mmu->hwpagetable);
1041 unsigned int pt_val;
1042
1043 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
1044 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
1045 return;
1046 }
1047 /* Mask off the lsb of the pt base address since lsb will not change */
1048 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
1049 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
1050 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
1051 for (i = 0; i < iommu->unit_count; i++) {
1052 /* get the lsb value which should not change when
1053 * changing ttbr0 */
1054 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
1055 KGSL_IOMMU_CONTEXT_USER);
1056 pt_val += pt_base;
1057
1058 KGSL_IOMMU_SET_IOMMU_REG(
1059 iommu->iommu_units[i].reg_map.hostptr,
1060 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
1061
1062 mb();
1063 temp = KGSL_IOMMU_GET_IOMMU_REG(
1064 iommu->iommu_units[i].reg_map.hostptr,
1065 KGSL_IOMMU_CONTEXT_USER, TTBR0);
1066 /* Set asid */
1067 KGSL_IOMMU_SET_IOMMU_REG(
1068 iommu->iommu_units[i].reg_map.hostptr,
1069 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
1070 kgsl_iommu_get_hwpagetable_asid(mmu));
1071 mb();
1072 temp = KGSL_IOMMU_GET_IOMMU_REG(
1073 iommu->iommu_units[i].reg_map.hostptr,
1074 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
1075 }
1076 }
1077 /* Flush tlb */
1078 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
1079 for (i = 0; i < iommu->unit_count; i++) {
1080 KGSL_IOMMU_SET_IOMMU_REG(
1081 iommu->iommu_units[i].reg_map.hostptr,
1082 KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
1083 kgsl_iommu_get_hwpagetable_asid(mmu));
1084 mb();
1085 }
1086 }
1087 /* Disable smmu clock */
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001088 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001089}
1090
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001091/*
1092 * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
1093 * the address of memory descriptors which map the IOMMU registers
1094 * @mmu - Pointer to mmu structure
1095 * @reg_map_desc - Out parameter in which the address of the array containing
1096 * pointers to register map descriptors is returned. The caller is supposed
1097 * to free this array
1098 *
1099 * Return - The number of iommu units which is also the number of register
1100 * mapped descriptor arrays which the out parameter will have
1101 */
1102static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
1103 void **reg_map_desc)
1104{
1105 struct kgsl_iommu *iommu = mmu->priv;
1106 void **reg_desc_ptr;
1107 int i;
1108
1109 /*
1110 * Alocate array of pointers that will hold address of the register map
1111 * descriptors
1112 */
1113 reg_desc_ptr = kmalloc(iommu->unit_count *
1114 sizeof(struct kgsl_memdesc *), GFP_KERNEL);
1115 if (!reg_desc_ptr) {
1116 KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
1117 iommu->unit_count * sizeof(struct kgsl_memdesc *));
1118 return -ENOMEM;
1119 }
1120
1121 for (i = 0; i < iommu->unit_count; i++)
1122 reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
1123
1124 *reg_map_desc = reg_desc_ptr;
1125 return i;
1126}
1127
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001128struct kgsl_mmu_ops iommu_ops = {
1129 .mmu_init = kgsl_iommu_init,
1130 .mmu_close = kgsl_iommu_close,
1131 .mmu_start = kgsl_iommu_start,
1132 .mmu_stop = kgsl_iommu_stop,
1133 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001134 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001135 .mmu_pagefault = NULL,
1136 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001137 .mmu_enable_clk = kgsl_iommu_enable_clk,
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001138 .mmu_disable_clk_on_ts = kgsl_iommu_disable_clk_on_ts,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001139 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001140 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001141 .mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001142};
1143
1144struct kgsl_mmu_pt_ops iommu_pt_ops = {
1145 .mmu_map = kgsl_iommu_map,
1146 .mmu_unmap = kgsl_iommu_unmap,
1147 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1148 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
1149 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das5a610b52012-05-09 17:31:54 -06001150 .mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001151};