blob: 71dfe8caa6b942ad8af530c05e93d59df619503b [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
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -070031static struct kgsl_iommu_register_list kgsl_iommuv1_reg[KGSL_IOMMU_REG_MAX] = {
32 { 0, 0, 0 }, /* GLOBAL_BASE */
33 { 0x10, 0x0003FFFF, 14 }, /* TTBR0 */
34 { 0x14, 0x0003FFFF, 14 }, /* TTBR1 */
35 { 0x20, 0, 0 }, /* FSR */
36 { 0x800, 0, 0 }, /* TLBIALL */
37};
38
Shubhraprakash Das15a8b462012-08-16 23:24:28 -070039static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = {
40 { 0, 0, 0 }, /* GLOBAL_BASE */
41 { 0x20, 0x00FFFFFF, 14 }, /* TTBR0 */
42 { 0x28, 0x00FFFFFF, 14 }, /* TTBR1 */
43 { 0x58, 0, 0 }, /* FSR */
44 { 0x618, 0, 0 } /* TLBIALL */
45};
46
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -070047static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out,
48 struct kgsl_iommu_unit **iommu_unit_out)
Jordan Crouse95b68472012-05-25 10:25:01 -060049{
50 int i, j, k;
51
52 for (i = 0; i < KGSL_DEVICE_MAX; i++) {
53 struct kgsl_mmu *mmu;
54 struct kgsl_iommu *iommu;
55
56 if (kgsl_driver.devp[i] == NULL)
57 continue;
58
59 mmu = kgsl_get_mmu(kgsl_driver.devp[i]);
60 if (mmu == NULL || mmu->priv == NULL)
61 continue;
62
63 iommu = mmu->priv;
64
65 for (j = 0; j < iommu->unit_count; j++) {
66 struct kgsl_iommu_unit *iommu_unit =
67 &iommu->iommu_units[j];
68 for (k = 0; k < iommu_unit->dev_count; k++) {
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -070069 if (iommu_unit->dev[k].dev == dev) {
70 *mmu_out = mmu;
71 *iommu_unit_out = iommu_unit;
72 return 0;
73 }
Jordan Crouse95b68472012-05-25 10:25:01 -060074 }
75 }
76 }
77
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -070078 return -EINVAL;
Jordan Crouse95b68472012-05-25 10:25:01 -060079}
80
81static struct kgsl_iommu_device *get_iommu_device(struct kgsl_iommu_unit *unit,
82 struct device *dev)
83{
84 int k;
85
86 for (k = 0; unit && k < unit->dev_count; k++) {
87 if (unit->dev[k].dev == dev)
88 return &(unit->dev[k]);
89 }
90
91 return NULL;
92}
93
94static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
95 struct device *dev, unsigned long addr, int flags)
96{
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -070097 int ret = 0;
98 struct kgsl_mmu *mmu;
99 struct kgsl_iommu *iommu;
100 struct kgsl_iommu_unit *iommu_unit;
101 struct kgsl_iommu_device *iommu_dev;
Jordan Crouse95b68472012-05-25 10:25:01 -0600102 unsigned int ptbase, fsr;
103
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700104 ret = get_iommu_unit(dev, &mmu, &iommu_unit);
105 if (ret)
106 goto done;
107 iommu_dev = get_iommu_device(iommu_unit, dev);
Jordan Crouse95b68472012-05-25 10:25:01 -0600108 if (!iommu_dev) {
109 KGSL_CORE_ERR("Invalid IOMMU device %p\n", dev);
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700110 ret = -ENOSYS;
111 goto done;
Jordan Crouse95b68472012-05-25 10:25:01 -0600112 }
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700113 iommu = mmu->priv;
Jordan Crouse95b68472012-05-25 10:25:01 -0600114
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700115 ptbase = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit,
Shubhraprakash Das190553a2012-06-11 15:17:59 -0600116 iommu_dev->ctx_id, TTBR0);
Jordan Crouse95b68472012-05-25 10:25:01 -0600117
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700118 fsr = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit,
Jordan Crouse95b68472012-05-25 10:25:01 -0600119 iommu_dev->ctx_id, FSR);
120
121 KGSL_MEM_CRIT(iommu_dev->kgsldev,
122 "GPU PAGE FAULT: addr = %lX pid = %d\n",
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700123 addr, kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase));
Jordan Crouse95b68472012-05-25 10:25:01 -0600124 KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
125 iommu_dev->ctx_id, fsr);
126
127 trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700128 kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), 0);
Jordan Crouse95b68472012-05-25 10:25:01 -0600129
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700130done:
131 return ret;
Jordan Crouse95b68472012-05-25 10:25:01 -0600132}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600133
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600134/*
135 * kgsl_iommu_disable_clk - Disable iommu clocks
136 * @mmu - Pointer to mmu structure
137 *
138 * Disables iommu clocks
139 * Return - void
140 */
141static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
142{
143 struct kgsl_iommu *iommu = mmu->priv;
144 struct msm_iommu_drvdata *iommu_drvdata;
145 int i, j;
146
147 for (i = 0; i < iommu->unit_count; i++) {
148 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
149 for (j = 0; j < iommu_unit->dev_count; j++) {
150 if (!iommu_unit->dev[j].clk_enabled)
151 continue;
152 iommu_drvdata = dev_get_drvdata(
153 iommu_unit->dev[j].dev->parent);
Shubhraprakash Das102aac52012-08-16 22:12:27 -0700154 if (iommu_drvdata->aclk)
155 clk_disable_unprepare(iommu_drvdata->aclk);
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600156 if (iommu_drvdata->clk)
157 clk_disable_unprepare(iommu_drvdata->clk);
158 clk_disable_unprepare(iommu_drvdata->pclk);
159 iommu_unit->dev[j].clk_enabled = false;
160 }
161 }
162}
163
164/*
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600165 * kgsl_iommu_disable_clk_event - An event function that is executed when
166 * the required timestamp is reached. It disables the IOMMU clocks if
167 * the timestamp on which the clocks can be disabled has expired.
168 * @device - The kgsl device pointer
169 * @data - The data passed during event creation, it is the MMU pointer
170 * @id - Context ID, should always be KGSL_MEMSTORE_GLOBAL
171 * @ts - The current timestamp that has expired for the device
172 *
173 * Disables IOMMU clocks if timestamp has expired
174 * Return - void
175 */
176static void kgsl_iommu_clk_disable_event(struct kgsl_device *device, void *data,
177 unsigned int id, unsigned int ts)
178{
179 struct kgsl_mmu *mmu = data;
180 struct kgsl_iommu *iommu = mmu->priv;
181
182 if (!iommu->clk_event_queued) {
183 if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts))
184 KGSL_DRV_ERR(device,
185 "IOMMU disable clock event being cancelled, "
186 "iommu_last_cmd_ts: %x, retired ts: %x\n",
187 iommu->iommu_last_cmd_ts, ts);
188 return;
189 }
190
191 if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) {
192 kgsl_iommu_disable_clk(mmu);
193 iommu->clk_event_queued = false;
194 } else {
195 /* add new event to fire when ts is reached, this can happen
196 * if we queued an event and someone requested the clocks to
197 * be disbaled on a later timestamp */
198 if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts,
199 kgsl_iommu_clk_disable_event, mmu, mmu)) {
200 KGSL_DRV_ERR(device,
201 "Failed to add IOMMU disable clk event\n");
202 iommu->clk_event_queued = false;
203 }
204 }
205}
206
207/*
208 * kgsl_iommu_disable_clk_on_ts - Sets up event to disable IOMMU clocks
209 * @mmu - The kgsl MMU pointer
210 * @ts - Timestamp on which the clocks should be disabled
211 * @ts_valid - Indicates whether ts parameter is valid, if this parameter
212 * is false then it means that the calling function wants to disable the
213 * IOMMU clocks immediately without waiting for any timestamp
214 *
215 * Creates an event to disable the IOMMU clocks on timestamp and if event
216 * already exists then updates the timestamp of disabling the IOMMU clocks
217 * with the passed in ts if it is greater than the current value at which
218 * the clocks will be disabled
219 * Return - void
220 */
221static void
222kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu, unsigned int ts,
223 bool ts_valid)
224{
225 struct kgsl_iommu *iommu = mmu->priv;
226
227 if (iommu->clk_event_queued) {
228 if (ts_valid && (0 <
229 timestamp_cmp(ts, iommu->iommu_last_cmd_ts)))
230 iommu->iommu_last_cmd_ts = ts;
231 } else {
232 if (ts_valid) {
233 iommu->iommu_last_cmd_ts = ts;
234 iommu->clk_event_queued = true;
235 if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
236 ts, kgsl_iommu_clk_disable_event, mmu, mmu)) {
237 KGSL_DRV_ERR(mmu->device,
238 "Failed to add IOMMU disable clk event\n");
239 iommu->clk_event_queued = false;
240 }
241 } else {
242 kgsl_iommu_disable_clk(mmu);
243 }
244 }
245}
246
247/*
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600248 * kgsl_iommu_enable_clk - Enable iommu clocks
249 * @mmu - Pointer to mmu structure
250 * @ctx_id - The context bank whose clocks are to be turned on
251 *
252 * Enables iommu clocks of a given context
253 * Return: 0 on success else error code
254 */
255static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
256 int ctx_id)
257{
258 int ret = 0;
259 int i, j;
260 struct kgsl_iommu *iommu = mmu->priv;
261 struct msm_iommu_drvdata *iommu_drvdata;
262
263 for (i = 0; i < iommu->unit_count; i++) {
264 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
265 for (j = 0; j < iommu_unit->dev_count; j++) {
266 if (iommu_unit->dev[j].clk_enabled ||
267 ctx_id != iommu_unit->dev[j].ctx_id)
268 continue;
269 iommu_drvdata =
270 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
271 ret = clk_prepare_enable(iommu_drvdata->pclk);
272 if (ret)
273 goto done;
274 if (iommu_drvdata->clk) {
275 ret = clk_prepare_enable(iommu_drvdata->clk);
276 if (ret) {
277 clk_disable_unprepare(
278 iommu_drvdata->pclk);
279 goto done;
280 }
281 }
Shubhraprakash Das102aac52012-08-16 22:12:27 -0700282 if (iommu_drvdata->aclk) {
283 ret = clk_prepare_enable(iommu_drvdata->aclk);
284 if (ret) {
285 if (iommu_drvdata->clk)
286 clk_disable_unprepare(
287 iommu_drvdata->clk);
288 clk_disable_unprepare(
289 iommu_drvdata->pclk);
290 goto done;
291 }
292 }
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600293 iommu_unit->dev[j].clk_enabled = true;
294 }
295 }
296done:
297 if (ret)
298 kgsl_iommu_disable_clk(mmu);
299 return ret;
300}
301
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600302/*
303 * kgsl_iommu_pt_equal - Check if pagetables are equal
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700304 * @mmu - Pointer to mmu structure
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600305 * @pt - Pointer to pagetable
306 * @pt_base - Address of a pagetable that the IOMMU register is
307 * programmed with
308 *
309 * Checks whether the pt_base is equal to the base address of
310 * the pagetable which is contained in the pt structure
311 * Return - Non-zero if the pagetable addresses are equal else 0
312 */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700313static int kgsl_iommu_pt_equal(struct kgsl_mmu *mmu,
314 struct kgsl_pagetable *pt,
315 unsigned int pt_base)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600316{
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700317 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600318 struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
319 unsigned int domain_ptbase = iommu_pt ?
320 iommu_get_pt_base_addr(iommu_pt->domain) : 0;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600321 /* Only compare the valid address bits of the pt_base */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700322 domain_ptbase &=
323 (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
324 iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
325
326 pt_base &=
327 (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
328 iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
329
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600330 return domain_ptbase && pt_base &&
331 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600332}
333
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600334/*
335 * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
336 * @mmu_specific_pt - Pointer to pagetable which is to be freed
337 *
338 * Return - void
339 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600340static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
341{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600342 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
343 if (iommu_pt->domain)
344 iommu_domain_free(iommu_pt->domain);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600345 kfree(iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600346}
347
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600348/*
349 * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
350 *
351 * Allocate memory to hold a pagetable and allocate the IOMMU
352 * domain which is the actual IOMMU pagetable
353 * Return - void
354 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600355void *kgsl_iommu_create_pagetable(void)
356{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600357 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600358
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600359 iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
360 if (!iommu_pt) {
361 KGSL_CORE_ERR("kzalloc(%d) failed\n",
362 sizeof(struct kgsl_iommu_pt));
363 return NULL;
364 }
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700365 /* L2 redirect is not stable on IOMMU v2 */
366 if (msm_soc_version_supports_iommu_v1())
367 iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700368 MSM_IOMMU_DOMAIN_PT_CACHEABLE);
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700369 else
370 iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
371 0);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600372 if (!iommu_pt->domain) {
373 KGSL_CORE_ERR("Failed to create iommu domain\n");
374 kfree(iommu_pt);
375 return NULL;
Jordan Crouse95b68472012-05-25 10:25:01 -0600376 } else {
377 iommu_set_fault_handler(iommu_pt->domain,
378 kgsl_iommu_fault_handler);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600379 }
Jordan Crouse95b68472012-05-25 10:25:01 -0600380
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600381 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600382}
383
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600384/*
385 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
386 * pagetable
387 * @mmu - Pointer to the device mmu structure
388 * @priv - Flag indicating whether the private or user context is to be
389 * detached
390 *
391 * Detach the IOMMU unit with the domain that is contained in the
392 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
393 * in use because the PTBR will not be set after a detach
394 * Return - void
395 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600396static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
397{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600398 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600399 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600400 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600401
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600402 for (i = 0; i < iommu->unit_count; i++) {
403 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600404 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600405 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600406 /*
407 * If there is a 2nd default pagetable then priv domain
408 * is attached with this pagetable
409 */
410 if (mmu->priv_bank_table &&
411 (KGSL_IOMMU_CONTEXT_PRIV == j))
412 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600413 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600414 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600415 iommu_unit->dev[j].dev);
416 iommu_unit->dev[j].attached = false;
417 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
418 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600419 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600420 }
421 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600422 }
423}
424
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600425/*
426 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
427 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
428 * setup other IOMMU registers for the device so that it becomes
429 * active
430 * @mmu - Pointer to the device mmu structure
431 * @priv - Flag indicating whether the private or user context is to be
432 * attached
433 *
434 * Attach the IOMMU unit with the domain that is contained in the
435 * hwpagetable of the given mmu.
436 * Return - 0 on success else error code
437 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600438static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
439{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600440 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600441 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600442 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600443
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600444 /*
445 * Loop through all the iommu devcies under all iommu units and
446 * attach the domain
447 */
448 for (i = 0; i < iommu->unit_count; i++) {
449 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600450 iommu_pt = mmu->defaultpagetable->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600451 for (j = 0; j < iommu_unit->dev_count; j++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600452 /*
453 * If there is a 2nd default pagetable then priv domain
454 * is attached to this pagetable
455 */
456 if (mmu->priv_bank_table &&
457 (KGSL_IOMMU_CONTEXT_PRIV == j))
458 iommu_pt = mmu->priv_bank_table->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600459 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600460 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600461 iommu_unit->dev[j].dev);
462 if (ret) {
463 KGSL_MEM_ERR(mmu->device,
464 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700465 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600466 goto done;
467 }
468 iommu_unit->dev[j].attached = true;
469 KGSL_MEM_INFO(mmu->device,
470 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600471 iommu_pt->domain, iommu_unit->dev[j].dev,
472 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700473 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600474 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600475 }
476done:
477 return ret;
478}
479
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600480/*
481 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
482 * @mmu - Pointer to mmu device
483 * data - Pointer to the platform data containing information about
484 * iommu devices for one iommu unit
485 * unit_id - The IOMMU unit number. This is not a specific ID but just
486 * a serial number. The serial numbers are treated as ID's of the
487 * IOMMU units
488 *
489 * Return - 0 on success else error code
490 */
491static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
492 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700493{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600494 struct kgsl_iommu *iommu = mmu->priv;
495 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700496 int i;
497
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600498 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
499 KGSL_CORE_ERR("Too many iommu devices defined for an "
500 "IOMMU unit\n");
501 return -EINVAL;
502 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700503
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600504 for (i = 0; i < data->iommu_ctx_count; i++) {
505 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700506 continue;
507
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600508 iommu_unit->dev[iommu_unit->dev_count].dev =
509 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
510 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
511 KGSL_CORE_ERR("Failed to get iommu dev handle for "
512 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700513 return -EINVAL;
514 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600515 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
516 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
517 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
518 data->iommu_ctxs[i].ctx_id);
519 return -EINVAL;
520 }
521 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
522 data->iommu_ctxs[i].ctx_id;
Jordan Crouse95b68472012-05-25 10:25:01 -0600523 iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
524
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600525 KGSL_DRV_INFO(mmu->device,
526 "Obtained dev handle %p for iommu context %s\n",
527 iommu_unit->dev[iommu_unit->dev_count].dev,
528 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700529
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600530 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700531 }
532
533 return 0;
534}
535
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600536/*
537 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
538 * @mmu - Pointer to mmu device
539 *
540 * Get the device pointers for the IOMMU user and priv contexts of the
541 * kgsl device
542 * Return - 0 on success else error code
543 */
544static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600545{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600546 struct platform_device *pdev =
547 container_of(mmu->device->parentdev, struct platform_device,
548 dev);
549 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
550 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700551 int i, ret = 0;
552
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600553 /* Go through the IOMMU data and get all the context devices */
554 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
555 KGSL_CORE_ERR("Too many IOMMU units defined\n");
556 ret = -EINVAL;
557 goto done;
558 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700559
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600560 for (i = 0; i < pdata_dev->iommu_count; i++) {
561 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700562 if (ret)
563 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600564 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600565 iommu->unit_count = pdata_dev->iommu_count;
566done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700567 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600568}
569
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600570/*
571 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
572 * of the respective iommu units
573 * @mmu - Pointer to mmu structure
574 *
575 * Return - 0 on success else error code
576 */
577static int kgsl_set_register_map(struct kgsl_mmu *mmu)
578{
579 struct platform_device *pdev =
580 container_of(mmu->device->parentdev, struct platform_device,
581 dev);
582 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
583 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
584 struct kgsl_iommu_unit *iommu_unit;
585 int i = 0, ret = 0;
586
587 for (; i < pdata_dev->iommu_count; i++) {
588 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
589 iommu_unit = &iommu->iommu_units[i];
590 /* set up the IOMMU register map for the given IOMMU unit */
591 if (!data.physstart || !data.physend) {
592 KGSL_CORE_ERR("The register range for IOMMU unit not"
593 " specified\n");
594 ret = -EINVAL;
595 goto err;
596 }
597 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
598 data.physend - data.physstart + 1);
599 if (!iommu_unit->reg_map.hostptr) {
600 KGSL_CORE_ERR("Failed to map SMMU register address "
601 "space from %x to %x\n", data.physstart,
602 data.physend - data.physstart + 1);
603 ret = -ENOMEM;
604 i--;
605 goto err;
606 }
607 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
608 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600609 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
610 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600611 }
612 iommu->unit_count = pdata_dev->iommu_count;
613 return ret;
614err:
615 /* Unmap any mapped IOMMU regions */
616 for (; i >= 0; i--) {
617 iommu_unit = &iommu->iommu_units[i];
618 iounmap(iommu_unit->reg_map.hostptr);
619 iommu_unit->reg_map.size = 0;
620 iommu_unit->reg_map.physaddr = 0;
621 }
622 return ret;
623}
624
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600625/*
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700626 * kgsl_iommu_get_pt_base_addr - Get the address of the pagetable that the
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600627 * IOMMU ttbr0 register is programmed with
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700628 * @mmu - Pointer to mmu
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600629 * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
630 *
631 * Return - actual pagetable address that the ttbr0 register is programmed
632 * with
633 */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700634static unsigned int kgsl_iommu_get_pt_base_addr(struct kgsl_mmu *mmu,
635 struct kgsl_pagetable *pt)
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600636{
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700637 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600638 struct kgsl_iommu_pt *iommu_pt = pt->priv;
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700639 return iommu_get_pt_base_addr(iommu_pt->domain) &
640 (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
641 iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600642}
643
644/*
645 * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
646 * @mmu - Pointer to mmu structure
647 * @hostptr - Pointer to the IOMMU register map. This is used to match
648 * the iommu device whose lsb value is to be returned
649 * @ctx_id - The context bank whose lsb valus is to be returned
650 * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
651 * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
652 * are only programmed once in the beginning when a domain is attached
653 * does not change.
654 */
655static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
656 unsigned int unit_id,
657 enum kgsl_iommu_context_id ctx_id)
658{
659 struct kgsl_iommu *iommu = mmu->priv;
660 int i, j;
661 for (i = 0; i < iommu->unit_count; i++) {
662 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
663 for (j = 0; j < iommu_unit->dev_count; j++)
664 if (unit_id == i &&
665 ctx_id == iommu_unit->dev[j].ctx_id)
666 return iommu_unit->dev[j].pt_lsb;
667 }
668 return 0;
669}
670
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600671static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Dasb2abc452012-06-08 16:33:03 -0600672 struct kgsl_pagetable *pagetable,
673 unsigned int context_id)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600674{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600675 if (mmu->flags & KGSL_FLAGS_STARTED) {
676 /* page table not current, then setup mmu to use new
677 * specified page table
678 */
679 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600680 unsigned int flags = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600681 mmu->hwpagetable = pagetable;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600682 flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
Shubhraprakash Das8649fa52012-07-26 15:49:46 -0700683 mmu->device->id) |
684 KGSL_MMUFLAGS_TLBFLUSH;
Shubhraprakash Dasb2abc452012-06-08 16:33:03 -0600685 kgsl_setstate(mmu, context_id,
686 KGSL_MMUFLAGS_PTUPDATE | flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600687 }
688 }
689}
690
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600691static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600692{
693 /*
694 * intialize device mmu
695 *
696 * call this with the global lock held
697 */
698 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600699 struct kgsl_iommu *iommu;
700
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600701 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
702 if (!iommu) {
703 KGSL_CORE_ERR("kzalloc(%d) failed\n",
704 sizeof(struct kgsl_iommu));
705 return -ENOMEM;
706 }
707
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600708 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600709 status = kgsl_get_iommu_ctxt(mmu);
710 if (status)
711 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600712 status = kgsl_set_register_map(mmu);
713 if (status)
714 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600715
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700716 iommu->iommu_reg_list = kgsl_iommuv1_reg;
717 iommu->ctx_offset = KGSL_IOMMU_CTX_OFFSET_V1;
718
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700719 if (msm_soc_version_supports_iommu_v1()) {
720 iommu->iommu_reg_list = kgsl_iommuv1_reg;
721 iommu->ctx_offset = KGSL_IOMMU_CTX_OFFSET_V1;
722 } else {
723 iommu->iommu_reg_list = kgsl_iommuv2_reg;
724 iommu->ctx_offset = KGSL_IOMMU_CTX_OFFSET_V2;
725 }
726
Shubhraprakash Dase9541a32012-05-09 22:25:55 -0600727 /* A nop is required in an indirect buffer when switching
728 * pagetables in-stream */
729 kgsl_sharedmem_writel(&mmu->setstate_memory,
730 KGSL_IOMMU_SETSTATE_NOP_OFFSET,
731 cp_nop_packet(1));
732
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600733 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600734 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600735done:
736 if (status) {
737 kfree(iommu);
738 mmu->priv = NULL;
739 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600740 return status;
741}
742
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600743/*
744 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
745 * for iommu. This function is only called once during first start, successive
746 * start do not call this funciton.
747 * @mmu - Pointer to mmu structure
748 *
749 * Create the initial defaultpagetable and setup the iommu mappings to it
750 * Return - 0 on success else error code
751 */
752static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
753{
754 int status = 0;
755 int i = 0;
756 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600757 struct kgsl_pagetable *pagetable = NULL;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600758
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600759 /* If chip is not 8960 then we use the 2nd context bank for pagetable
760 * switching on the 3D side for which a separate table is allocated */
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700761 if (!cpu_is_msm8960() && msm_soc_version_supports_iommu_v1()) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600762 mmu->priv_bank_table =
763 kgsl_mmu_getpagetable(KGSL_MMU_PRIV_BANK_TABLE_NAME);
764 if (mmu->priv_bank_table == NULL) {
765 status = -ENOMEM;
766 goto err;
767 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600768 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600769 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
770 /* Return error if the default pagetable doesn't exist */
771 if (mmu->defaultpagetable == NULL) {
772 status = -ENOMEM;
773 goto err;
774 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600775 pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
776 mmu->defaultpagetable;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600777 /* Map the IOMMU regsiters to only defaultpagetable */
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700778 if (msm_soc_version_supports_iommu_v1()) {
779 for (i = 0; i < iommu->unit_count; i++) {
780 iommu->iommu_units[i].reg_map.priv |=
781 KGSL_MEMFLAGS_GLOBAL;
782 status = kgsl_mmu_map(pagetable,
783 &(iommu->iommu_units[i].reg_map),
784 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
785 if (status) {
786 iommu->iommu_units[i].reg_map.priv &=
787 ~KGSL_MEMFLAGS_GLOBAL;
788 goto err;
789 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600790 }
791 }
792 return status;
793err:
794 for (i--; i >= 0; i--) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600795 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600796 &(iommu->iommu_units[i].reg_map));
797 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
798 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600799 if (mmu->priv_bank_table) {
800 kgsl_mmu_putpagetable(mmu->priv_bank_table);
801 mmu->priv_bank_table = NULL;
802 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600803 if (mmu->defaultpagetable) {
804 kgsl_mmu_putpagetable(mmu->defaultpagetable);
805 mmu->defaultpagetable = NULL;
806 }
807 return status;
808}
809
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600810static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600811{
812 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600813 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600814 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600815
816 if (mmu->flags & KGSL_FLAGS_STARTED)
817 return 0;
818
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600819 if (mmu->defaultpagetable == NULL) {
820 status = kgsl_iommu_setup_defaultpagetable(mmu);
821 if (status)
822 return -ENOMEM;
823 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600824 /* We use the GPU MMU to control access to IOMMU registers on 8960 with
825 * a225, hence we still keep the MMU active on 8960 */
826 if (cpu_is_msm8960()) {
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600827 struct kgsl_mh *mh = &(mmu->device->mh);
828 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
829 kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
830 mh->mpu_base +
Shubhraprakash Das2b54b5f2012-08-08 18:21:36 -0700831 iommu->iommu_units[0].reg_map.gpuaddr);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600832 } else {
833 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
834 }
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600835
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600836 mmu->hwpagetable = mmu->defaultpagetable;
837
838 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600839 if (status) {
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600840 mmu->hwpagetable = NULL;
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600841 goto done;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600842 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600843 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600844 if (status) {
845 KGSL_CORE_ERR("clk enable failed\n");
846 goto done;
847 }
848 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
849 if (status) {
850 KGSL_CORE_ERR("clk enable failed\n");
851 goto done;
852 }
853 /* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
854 * that value should not change when we change pagetables, so while
855 * changing pagetables we can use this lsb value of the pagetable w/o
856 * having to read it again
857 */
858 for (i = 0; i < iommu->unit_count; i++) {
859 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
860 for (j = 0; j < iommu_unit->dev_count; j++)
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700861 iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(iommu,
862 KGSL_IOMMU_GET_CTX_REG(iommu,
863 iommu_unit,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600864 iommu_unit->dev[j].ctx_id,
865 TTBR0));
866 }
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600867
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600868 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasbb5ad2a2012-05-09 22:58:52 -0600869 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600870
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600871done:
872 if (status) {
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600873 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -0600874 kgsl_detach_pagetable_iommu_domain(mmu);
875 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600876 return status;
877}
878
879static int
880kgsl_iommu_unmap(void *mmu_specific_pt,
Shubhraprakash Das0c811262012-06-06 23:22:19 -0600881 struct kgsl_memdesc *memdesc,
882 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600883{
884 int ret;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600885 unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600886 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600887
888 /* All GPU addresses as assigned are page aligned, but some
889 functions purturb the gpuaddr with an offset, so apply the
890 mask here to make sure we have the right address */
891
892 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
893
894 if (range == 0 || gpuaddr == 0)
895 return 0;
896
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600897 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600898 if (ret)
899 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600900 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600901 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600902
Shubhraprakash Das0c811262012-06-06 23:22:19 -0600903#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
904 /*
905 * Flushing only required if per process pagetables are used. With
906 * global case, flushing will happen inside iommu_map function
907 */
Shubhraprakash Das15a8b462012-08-16 23:24:28 -0700908 if (!ret && msm_soc_version_supports_iommu_v1())
Shubhraprakash Das0c811262012-06-06 23:22:19 -0600909 *tlb_flags = UINT_MAX;
910#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600911 return 0;
912}
913
914static int
915kgsl_iommu_map(void *mmu_specific_pt,
916 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600917 unsigned int protflags,
918 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600919{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600920 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600921 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600922 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600923 int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600924
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600925 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600926
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600927
Jordan Croused17e9aa2011-10-12 16:57:48 -0600928 iommu_virt_addr = memdesc->gpuaddr;
929
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600930 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600931 size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600932 if (ret) {
933 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600934 "failed with err: %d\n", iommu_pt->domain,
Jordan Crouse3c86ca82012-05-21 08:41:52 -0600935 iommu_virt_addr, memdesc->sg, size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600936 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600937 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600938 }
939
940 return ret;
941}
942
Shubhraprakash Das79447952012-04-26 18:12:23 -0600943static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600944{
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600945 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600946 /*
947 * stop device mmu
948 *
949 * call this with the global lock held
950 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600951
952 if (mmu->flags & KGSL_FLAGS_STARTED) {
953 /* detach iommu attachment */
954 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600955 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600956
957 mmu->flags &= ~KGSL_FLAGS_STARTED;
958 }
Shubhraprakash Dascb068072012-06-07 17:52:41 -0600959
960 /* switch off MMU clocks and cancel any events it has queued */
961 iommu->clk_event_queued = false;
962 kgsl_cancel_events(mmu->device, mmu);
963 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600964}
965
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600966static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600967{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600968 struct kgsl_iommu *iommu = mmu->priv;
969 int i;
970 for (i = 0; i < iommu->unit_count; i++) {
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600971 struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
972 mmu->priv_bank_table : mmu->defaultpagetable);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600973 if (iommu->iommu_units[i].reg_map.gpuaddr)
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600974 kgsl_mmu_unmap(pagetable,
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600975 &(iommu->iommu_units[i].reg_map));
976 if (iommu->iommu_units[i].reg_map.hostptr)
977 iounmap(iommu->iommu_units[i].reg_map.hostptr);
978 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
979 iommu->iommu_units[i].reg_map.sglen);
980 }
Shubhraprakash Das19ca4a62012-05-18 12:11:20 -0600981
982 if (mmu->priv_bank_table)
983 kgsl_mmu_putpagetable(mmu->priv_bank_table);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600984 if (mmu->defaultpagetable)
985 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600986 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600987
988 return 0;
989}
990
991static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600992kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600993{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600994 unsigned int pt_base;
995 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das64a745c2012-06-11 16:44:16 -0600996 /* We cannot enable or disable the clocks in interrupt context, this
997 function is called from interrupt context if there is an axi error */
998 if (in_interrupt())
999 return 0;
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -06001000 /* Return the current pt base by reading IOMMU pt_base register */
1001 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001002 pt_base = KGSL_IOMMU_GET_CTX_REG(iommu, (&iommu->iommu_units[0]),
1003 KGSL_IOMMU_CONTEXT_USER,
1004 TTBR0);
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001005 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001006 return pt_base &
1007 (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
1008 iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001009}
1010
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -06001011/*
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001012 * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
1013 * of the primary context bank
1014 * @mmu - Pointer to mmu structure
1015 * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
1016 * flushed or both
1017 *
1018 * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
1019 * do both by doing direct register writes to the IOMMu registers through the
1020 * cpu
1021 * Return - void
1022 */
1023static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
1024 uint32_t flags)
1025{
1026 struct kgsl_iommu *iommu = mmu->priv;
1027 int temp;
1028 int i;
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001029 unsigned int pt_base = kgsl_iommu_get_pt_base_addr(mmu,
1030 mmu->hwpagetable);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001031 unsigned int pt_val;
1032
1033 if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
1034 KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
1035 return;
1036 }
1037 /* Mask off the lsb of the pt base address since lsb will not change */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001038 pt_base &= (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
1039 iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
1040
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001041 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
Jordan Crousea29a2e02012-08-14 09:09:23 -06001042 kgsl_idle(mmu->device);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001043 for (i = 0; i < iommu->unit_count; i++) {
1044 /* get the lsb value which should not change when
1045 * changing ttbr0 */
1046 pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
1047 KGSL_IOMMU_CONTEXT_USER);
1048 pt_val += pt_base;
1049
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001050 KGSL_IOMMU_SET_CTX_REG(iommu, (&iommu->iommu_units[i]),
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001051 KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
1052
1053 mb();
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001054 temp = KGSL_IOMMU_GET_CTX_REG(iommu,
1055 (&iommu->iommu_units[i]),
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001056 KGSL_IOMMU_CONTEXT_USER, TTBR0);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001057 }
1058 }
1059 /* Flush tlb */
1060 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
1061 for (i = 0; i < iommu->unit_count; i++) {
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001062 KGSL_IOMMU_SET_CTX_REG(iommu, (&iommu->iommu_units[i]),
1063 KGSL_IOMMU_CONTEXT_USER, TLBIALL, 1);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001064 mb();
1065 }
1066 }
1067 /* Disable smmu clock */
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001068 kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001069}
1070
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001071/*
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001072 * kgsl_iommu_get_reg_gpuaddr - Returns the gpu address of IOMMU regsiter
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001073 * @mmu - Pointer to mmu structure
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001074 * @iommu_unit - The iommu unit for which base address is requested
1075 * @ctx_id - The context ID of the IOMMU ctx
1076 * @reg - The register for which address is required
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001077 *
1078 * Return - The number of iommu units which is also the number of register
1079 * mapped descriptor arrays which the out parameter will have
1080 */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001081static unsigned int kgsl_iommu_get_reg_gpuaddr(struct kgsl_mmu *mmu,
1082 int iommu_unit, int ctx_id, int reg)
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001083{
1084 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001085
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001086 if (KGSL_IOMMU_GLOBAL_BASE == reg)
1087 return iommu->iommu_units[iommu_unit].reg_map.gpuaddr;
1088 else
1089 return iommu->iommu_units[iommu_unit].reg_map.gpuaddr +
1090 iommu->iommu_reg_list[reg].reg_offset +
1091 (ctx_id << KGSL_IOMMU_CTX_SHIFT) + iommu->ctx_offset;
1092}
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001093
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001094static int kgsl_iommu_get_num_iommu_units(struct kgsl_mmu *mmu)
1095{
1096 struct kgsl_iommu *iommu = mmu->priv;
1097 return iommu->unit_count;
Shubhraprakash Dasa5b1db42012-05-09 18:02:34 -06001098}
1099
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001100struct kgsl_mmu_ops iommu_ops = {
1101 .mmu_init = kgsl_iommu_init,
1102 .mmu_close = kgsl_iommu_close,
1103 .mmu_start = kgsl_iommu_start,
1104 .mmu_stop = kgsl_iommu_stop,
1105 .mmu_setstate = kgsl_iommu_setstate,
Shubhraprakash Dasd8cbcd12012-05-07 16:11:32 -06001106 .mmu_device_setstate = kgsl_iommu_default_setstate,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001107 .mmu_pagefault = NULL,
1108 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -06001109 .mmu_enable_clk = kgsl_iommu_enable_clk,
Shubhraprakash Dascb068072012-06-07 17:52:41 -06001110 .mmu_disable_clk_on_ts = kgsl_iommu_disable_clk_on_ts,
Shubhraprakash Dasfce27362012-05-09 17:44:14 -06001111 .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -07001112 .mmu_get_reg_gpuaddr = kgsl_iommu_get_reg_gpuaddr,
1113 .mmu_get_num_iommu_units = kgsl_iommu_get_num_iommu_units,
1114 .mmu_pt_equal = kgsl_iommu_pt_equal,
1115 .mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001116};
1117
1118struct kgsl_mmu_pt_ops iommu_pt_ops = {
1119 .mmu_map = kgsl_iommu_map,
1120 .mmu_unmap = kgsl_iommu_unmap,
1121 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
1122 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06001123};