blob: 25d046326b6daceaf30e6f1edece2681581dd4e9 [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 Dasb2abc452012-06-08 16:33:03 -0600620 struct kgsl_pagetable *pagetable,
621 unsigned int context_id)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600622{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600623 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600624 struct kgsl_iommu *iommu = mmu->priv;
625 struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600626 /* page table not current, then setup mmu to use new
627 * specified page table
628 */
629 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600630 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600631 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600632 /* force tlb flush if asid is reused */
633 if (iommu->asid_reuse &&
634 (KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
635 flags |= KGSL_MMUFLAGS_TLBFLUSH;
636 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
637 mmu->device->id);
Shubhraprakash Dasb2abc452012-06-08 16:33:03 -0600638 kgsl_setstate(mmu, context_id,
639 KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600640 }
641 }
642}
643
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600644static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600645{
646 /*
647 * intialize device mmu
648 *
649 * call this with the global lock held
650 */
651 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600652 struct kgsl_iommu *iommu;
653
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600654 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
655 if (!iommu) {
656 KGSL_CORE_ERR("kzalloc(%d) failed\n",
657 sizeof(struct kgsl_iommu));
658 return -ENOMEM;
659 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600660 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
661 sizeof(unsigned long), GFP_KERNEL);
662 if (!iommu->asids) {
663 KGSL_CORE_ERR("kzalloc(%d) failed\n",
664 sizeof(struct kgsl_iommu));
665 status = -ENOMEM;
666 goto done;
667 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600668
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600669 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600670 status = kgsl_get_iommu_ctxt(mmu);
671 if (status)
672 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600673 status = kgsl_set_register_map(mmu);
674 if (status)
675 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600676
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600677 /* A nop is required in an indirect buffer when switching
678 * pagetables in-stream */
679 kgsl_sharedmem_writel(&mmu->setstate_memory,
680 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
681 cp_nop_packet(1));
682
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600683 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600684 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600685done:
686 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600687 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600688 kfree(iommu);
689 mmu->priv = NULL;
690 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600691 return status;
692}
693
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600694/*
695 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
696 * for iommu. This function is only called once during first start, successive
697 * start do not call this funciton.
698 * @mmu - Pointer to mmu structure
699 *
700 * Create the initial defaultpagetable and setup the iommu mappings to it
701 * Return - 0 on success else error code
702 */
703static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
704{
705 int status = 0;
706 int i = 0;
707 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600708 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600709 struct kgsl_pagetable *pagetable = NULL;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600710
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600711 /* If chip is not 8960 then we use the 2nd context bank for pagetable
712 * switching on the 3D side for which a separate table is allocated */
713 if (!cpu_is_msm8960()) {
714 mmu->priv_bank_table =
715 kgsl_mmu_getpagetable(KGSL_MMU_PRIV_BANK_TABLE_NAME);
716 if (mmu->priv_bank_table == NULL) {
717 status = -ENOMEM;
718 goto err;
719 }
720 iommu_pt = mmu->priv_bank_table->priv;
721 iommu_pt->asid = 1;
722 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600723 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
724 /* Return error if the default pagetable doesn't exist */
725 if (mmu->defaultpagetable == NULL) {
726 status = -ENOMEM;
727 goto err;
728 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600729 pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
730 mmu->defaultpagetable;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600731 /* Map the IOMMU regsiters to only defaultpagetable */
732 for (i = 0; i < iommu->unit_count; i++) {
733 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600734 status = kgsl_mmu_map(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600735 &(iommu->iommu_units[i].reg_map),
736 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
737 if (status) {
738 iommu->iommu_units[i].reg_map.priv &=
739 ~KGSL_MEMFLAGS_GLOBAL;
740 goto err;
741 }
742 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600743 /*
744 * The dafault pagetable always has asid 0 assigned by the iommu driver
745 * and asid 1 is assigned to the private context.
746 */
747 iommu_pt = mmu->defaultpagetable->priv;
748 iommu_pt->asid = 0;
749 set_bit(0, iommu->asids);
750 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600751 return status;
752err:
753 for (i--; i >= 0; i--) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600754 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600755 &(iommu->iommu_units[i].reg_map));
756 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
757 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600758 if (mmu->priv_bank_table) {
759 kgsl_mmu_putpagetable(mmu->priv_bank_table);
760 mmu->priv_bank_table = NULL;
761 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600762 if (mmu->defaultpagetable) {
763 kgsl_mmu_putpagetable(mmu->defaultpagetable);
764 mmu->defaultpagetable = NULL;
765 }
766 return status;
767}
768
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600769static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600770{
771 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600772 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600773 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600774
775 if (mmu->flags & KGSL_FLAGS_STARTED)
776 return 0;
777
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600778 if (mmu->defaultpagetable == NULL) {
779 status = kgsl_iommu_setup_defaultpagetable(mmu);
780 if (status)
781 return -ENOMEM;
782 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600783 /* We use the GPU MMU to control access to IOMMU registers on 8960 with
784 * a225, hence we still keep the MMU active on 8960 */
785 if (cpu_is_msm8960()) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600786 struct kgsl_mh *mh = &(mmu->device->mh);
787 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
788 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
789 mh->mpu_base +
Shubhraprakash Dasc6e21012012-05-11 17:24:51 -0600790 iommu->iommu_units
791 [iommu->unit_count - 1].reg_map.gpuaddr -
792 PAGE_SIZE);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600793 } else {
794 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
795 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600796
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600797 mmu->hwpagetable = mmu->defaultpagetable;
798
799 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600800 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600801 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600802 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600803 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600804 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600805 if (status) {
806 KGSL_CORE_ERR("clk enable failed\n");
807 goto done;
808 }
809 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
810 if (status) {
811 KGSL_CORE_ERR("clk enable failed\n");
812 goto done;
813 }
814 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
815 * that value should not change when we change pagetables, so while
816 * changing pagetables we can use this lsb value of the pagetable w/o
817 * having to read it again
818 */
819 for (i = 0; i < iommu->unit_count; i++) {
820 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600821 /* Make sure that the ASID of the priv bank is set to 1.
822 * When we a different pagetable for the priv bank then the
823 * iommu driver sets the ASID to 0 instead of 1 */
824 KGSL_IOMMU_SET_IOMMU_REG(iommu->iommu_units[i].reg_map.hostptr,
825 KGSL_IOMMU_CONTEXT_PRIV,
826 CONTEXTIDR, 1);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600827 for (j = 0; j < iommu_unit->dev_count; j++)
828 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
829 KGSL_IOMMU_GET_IOMMU_REG(
830 iommu_unit->reg_map.hostptr,
831 iommu_unit->dev[j].ctx_id,
832 TTBR0));
833 }
834 iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
835 iommu->iommu_units[0].reg_map.hostptr,
836 KGSL_IOMMU_CONTEXT_USER,
837 CONTEXTIDR);
838
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600839 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600840 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600841
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600842done:
843 if (status) {
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600844 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600845 kgsl_detach_pagetable_iommu_domain(mmu);
846 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600847 return status;
848}
849
850static int
851kgsl_iommu_unmap(void *mmu_specific_pt,
Shubhraprakash Das0c811262012-06-06 23:22:19 -0600852 struct kgsl_memdesc *memdesc,
853 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600854{
855 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600856 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600857 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600858
859 /* All GPU addresses as assigned are page aligned, but some
860 functions purturb the gpuaddr with an offset, so apply the
861 mask here to make sure we have the right address */
862
863 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
864
865 if (range == 0 || gpuaddr == 0)
866 return 0;
867
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600868 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600869 if (ret)
870 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600871 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600872 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600873
Shubhraprakash Das0c811262012-06-06 23:22:19 -0600874#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
875 /*
876 * Flushing only required if per process pagetables are used. With
877 * global case, flushing will happen inside iommu_map function
878 */
879 if (!ret)
880 *tlb_flags = UINT_MAX;
881#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600882 return 0;
883}
884
885static int
886kgsl_iommu_map(void *mmu_specific_pt,
887 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600888 unsigned int protflags,
889 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600890{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600891 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600892 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600893 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600894 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600895
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600896 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600897
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600898
Jordan Croused17e9aa2011-10-12 16:57:48 -0600899 iommu_virt_addr = memdesc->gpuaddr;
900
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600901 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600902 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600903 if (ret) {
904 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600905 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600906 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600907 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600908 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600909 }
910
911 return ret;
912}
913
Shubhraprakash Das79447952012-04-26 18:12:23 -0600914static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600915{
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600916 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600917 /*
918 * stop device mmu
919 *
920 * call this with the global lock held
921 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600922
923 if (mmu->flags & KGSL_FLAGS_STARTED) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600924 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600925 /* detach iommu attachment */
926 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600927 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600928
929 mmu->flags &= ~KGSL_FLAGS_STARTED;
930 }
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600931
932 /* switch off MMU clocks and cancel any events it has queued */
933 iommu->clk_event_queued = false;
934 kgsl_cancel_events(mmu->device, mmu);
935 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600936}
937
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600938static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600939{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600940 struct kgsl_iommu *iommu = mmu->priv;
941 int i;
942 for (i = 0; i < iommu->unit_count; i++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600943 struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
944 mmu->priv_bank_table : mmu->defaultpagetable);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600945 if (iommu->iommu_units[i].reg_map.gpuaddr)
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600946 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600947 &(iommu->iommu_units[i].reg_map));
948 if (iommu->iommu_units[i].reg_map.hostptr)
949 iounmap(iommu->iommu_units[i].reg_map.hostptr);
950 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
951 iommu->iommu_units[i].reg_map.sglen);
952 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600953
954 if (mmu->priv_bank_table)
955 kgsl_mmu_putpagetable(mmu->priv_bank_table);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600956 if (mmu->defaultpagetable)
957 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600958 kfree(iommu->asids);
959 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600960
961 return 0;
962}
963
964static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600965kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600966{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600967 unsigned int pt_base;
968 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das64a745c2012-06-11 16:44:16 -0600969 /* We cannot enable or disable the clocks in interrupt context, this
970 function is called from interrupt context if there is an axi error */
971 if (in_interrupt())
972 return 0;
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600973 /* Return the current pt base by reading IOMMU pt_base register */
974 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
975 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
976 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
977 KGSL_IOMMU_TTBR0);
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600978 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600979 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
980 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600981}
982
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600983/*
984 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
985 * pagetable
986 * @mmu - Pointer to mmu structure
987 *
988 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
989 * are unique identifiers for pagetable that can be used to selectively flush
990 * tlb entries of the IOMMU unit.
991 * Return - asid to be used with the IOMMU domain
992 */
993static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
994{
995 struct kgsl_iommu *iommu = mmu->priv;
996 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
997
998 /*
999 * If the iommu pagetable does not have any asid assigned and is not the
1000 * default pagetable then assign asid.
1001 */
1002 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
1003 iommu_pt->asid = find_first_zero_bit(iommu->asids,
1004 KGSL_IOMMU_MAX_ASIDS);
1005 /* No free bits means reuse asid */
1006 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
1007 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
1008 iommu->asid_reuse++;
1009 }
1010 set_bit(iommu_pt->asid, iommu->asids);
1011 /*
1012 * Store pointer to asids list so that during pagetable destroy
1013 * the asid assigned to this pagetable may be cleared
1014 */
1015 iommu_pt->iommu = iommu;
1016 }
1017 /* Return the asid + the constant part of asid that never changes */
1018 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
1019 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
1020 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
1021 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
1022}
1023
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001024/*
1025 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
1026 * of the primary context bank
1027 * @mmu - Pointer to mmu structure
1028 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
1029 * flushed or both
1030 *
1031 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
1032 * do both by doing direct register writes to the IOMMu registers through the
1033 * cpu
1034 * Return - void
1035 */
1036static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
1037 uint32_t flags)
1038{
1039 struct kgsl_iommu *iommu = mmu->priv;
1040 int temp;
1041 int i;
1042 unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
1043 mmu->hwpagetable);
1044 unsigned int pt_val;
1045
1046 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
1047 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
1048 return;
1049 }
1050 /* Mask off the lsb of the pt base address since lsb will not change */
1051 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
1052 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
1053 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
1054 for (i = 0; i < iommu->unit_count; i++) {
1055 /* get the lsb value which should not change when
1056 * changing ttbr0 */
1057 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
1058 KGSL_IOMMU_CONTEXT_USER);
1059 pt_val += pt_base;
1060
1061 KGSL_IOMMU_SET_IOMMU_REG(
1062 iommu->iommu_units[i].reg_map.hostptr,
1063 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
1064
1065 mb();
1066 temp = KGSL_IOMMU_GET_IOMMU_REG(
1067 iommu->iommu_units[i].reg_map.hostptr,
1068 KGSL_IOMMU_CONTEXT_USER, TTBR0);
1069 /* Set asid */
1070 KGSL_IOMMU_SET_IOMMU_REG(
1071 iommu->iommu_units[i].reg_map.hostptr,
1072 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
1073 kgsl_iommu_get_hwpagetable_asid(mmu));
1074 mb();
1075 temp = KGSL_IOMMU_GET_IOMMU_REG(
1076 iommu->iommu_units[i].reg_map.hostptr,
1077 KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
1078 }
1079 }
1080 /* Flush tlb */
1081 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
1082 for (i = 0; i < iommu->unit_count; i++) {
1083 KGSL_IOMMU_SET_IOMMU_REG(
1084 iommu->iommu_units[i].reg_map.hostptr,
1085 KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
1086 kgsl_iommu_get_hwpagetable_asid(mmu));
1087 mb();
1088 }
1089 }
1090 /* Disable smmu clock */
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001091 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001092}
1093
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001094/*
1095 * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
1096 * the address of memory descriptors which map the IOMMU registers
1097 * @mmu - Pointer to mmu structure
1098 * @reg_map_desc - Out parameter in which the address of the array containing
1099 * pointers to register map descriptors is returned. The caller is supposed
1100 * to free this array
1101 *
1102 * Return - The number of iommu units which is also the number of register
1103 * mapped descriptor arrays which the out parameter will have
1104 */
1105static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
1106 void **reg_map_desc)
1107{
1108 struct kgsl_iommu *iommu = mmu->priv;
1109 void **reg_desc_ptr;
1110 int i;
1111
1112 /*
1113 * Alocate array of pointers that will hold address of the register map
1114 * descriptors
1115 */
1116 reg_desc_ptr = kmalloc(iommu->unit_count *
1117 sizeof(struct kgsl_memdesc *), GFP_KERNEL);
1118 if (!reg_desc_ptr) {
1119 KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
1120 iommu->unit_count * sizeof(struct kgsl_memdesc *));
1121 return -ENOMEM;
1122 }
1123
1124 for (i = 0; i < iommu->unit_count; i++)
1125 reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
1126
1127 *reg_map_desc = reg_desc_ptr;
1128 return i;
1129}
1130
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001131struct kgsl_mmu_ops iommu_ops = {
1132 .mmu_init = kgsl_iommu_init,
1133 .mmu_close = kgsl_iommu_close,
1134 .mmu_start = kgsl_iommu_start,
1135 .mmu_stop = kgsl_iommu_stop,
1136 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001137 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001138 .mmu_pagefault = NULL,
1139 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001140 .mmu_enable_clk = kgsl_iommu_enable_clk,
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001141 .mmu_disable_clk_on_ts = kgsl_iommu_disable_clk_on_ts,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001142 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001143 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001144 .mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001145};
1146
1147struct kgsl_mmu_pt_ops iommu_pt_ops = {
1148 .mmu_map = kgsl_iommu_map,
1149 .mmu_unmap = kgsl_iommu_unmap,
1150 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1151 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
1152 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das5a610b52012-05-09 17:31:54 -06001153 .mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001154};