blob: 1598e16db3fd7e581c9f326d7c7987a55e873312 [file] [log] [blame]
Shubhraprakash Das79447952012-04-26 18:12:23 -06001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Shubhraprakash Das767fdda2011-08-15 15:49:45 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#include <linux/types.h>
14#include <linux/device.h>
15#include <linux/spinlock.h>
16#include <linux/genalloc.h>
17#include <linux/slab.h>
18#include <linux/iommu.h>
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060019#include <linux/msm_kgsl.h>
20
21#include "kgsl.h"
22#include "kgsl_device.h"
23#include "kgsl_mmu.h"
24#include "kgsl_sharedmem.h"
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -060025#include "kgsl_iommu.h"
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060026
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -060027/*
28 * kgsl_iommu_disable_clk - Disable iommu clocks
29 * @mmu - Pointer to mmu structure
30 *
31 * Disables iommu clocks
32 * Return - void
33 */
34static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
35{
36 struct kgsl_iommu *iommu = mmu->priv;
37 struct msm_iommu_drvdata *iommu_drvdata;
38 int i, j;
39
40 for (i = 0; i < iommu->unit_count; i++) {
41 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
42 for (j = 0; j < iommu_unit->dev_count; j++) {
43 if (!iommu_unit->dev[j].clk_enabled)
44 continue;
45 iommu_drvdata = dev_get_drvdata(
46 iommu_unit->dev[j].dev->parent);
47 if (iommu_drvdata->clk)
48 clk_disable_unprepare(iommu_drvdata->clk);
49 clk_disable_unprepare(iommu_drvdata->pclk);
50 iommu_unit->dev[j].clk_enabled = false;
51 }
52 }
53}
54
55/*
56 * kgsl_iommu_enable_clk - Enable iommu clocks
57 * @mmu - Pointer to mmu structure
58 * @ctx_id - The context bank whose clocks are to be turned on
59 *
60 * Enables iommu clocks of a given context
61 * Return: 0 on success else error code
62 */
63static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
64 int ctx_id)
65{
66 int ret = 0;
67 int i, j;
68 struct kgsl_iommu *iommu = mmu->priv;
69 struct msm_iommu_drvdata *iommu_drvdata;
70
71 for (i = 0; i < iommu->unit_count; i++) {
72 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
73 for (j = 0; j < iommu_unit->dev_count; j++) {
74 if (iommu_unit->dev[j].clk_enabled ||
75 ctx_id != iommu_unit->dev[j].ctx_id)
76 continue;
77 iommu_drvdata =
78 dev_get_drvdata(iommu_unit->dev[j].dev->parent);
79 ret = clk_prepare_enable(iommu_drvdata->pclk);
80 if (ret)
81 goto done;
82 if (iommu_drvdata->clk) {
83 ret = clk_prepare_enable(iommu_drvdata->clk);
84 if (ret) {
85 clk_disable_unprepare(
86 iommu_drvdata->pclk);
87 goto done;
88 }
89 }
90 iommu_unit->dev[j].clk_enabled = true;
91 }
92 }
93done:
94 if (ret)
95 kgsl_iommu_disable_clk(mmu);
96 return ret;
97}
98
Shubhraprakash Das48d97302012-05-07 12:16:08 -060099/*
100 * kgsl_iommu_pt_equal - Check if pagetables are equal
101 * @pt - Pointer to pagetable
102 * @pt_base - Address of a pagetable that the IOMMU register is
103 * programmed with
104 *
105 * Checks whether the pt_base is equal to the base address of
106 * the pagetable which is contained in the pt structure
107 * Return - Non-zero if the pagetable addresses are equal else 0
108 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600109static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
110 unsigned int pt_base)
111{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600112 struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
113 unsigned int domain_ptbase = iommu_pt ?
114 iommu_get_pt_base_addr(iommu_pt->domain) : 0;
Shubhraprakash Das48d97302012-05-07 12:16:08 -0600115 /* Only compare the valid address bits of the pt_base */
116 domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
117 KGSL_IOMMU_TTBR0_PA_SHIFT);
118 pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
119 KGSL_IOMMU_TTBR0_PA_SHIFT);
120 return domain_ptbase && pt_base &&
121 (domain_ptbase == pt_base);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600122}
123
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600124/*
125 * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
126 * @mmu_specific_pt - Pointer to pagetable which is to be freed
127 *
128 * Return - void
129 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600130static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
131{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600132 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
133 if (iommu_pt->domain)
134 iommu_domain_free(iommu_pt->domain);
135 if (iommu_pt->iommu) {
136 if ((KGSL_IOMMU_ASID_REUSE == iommu_pt->asid) &&
137 iommu_pt->iommu->asid_reuse)
138 iommu_pt->iommu->asid_reuse--;
139 if (!iommu_pt->iommu->asid_reuse ||
140 (KGSL_IOMMU_ASID_REUSE != iommu_pt->asid))
141 clear_bit(iommu_pt->asid, iommu_pt->iommu->asids);
142 }
143 kfree(iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600144}
145
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600146/*
147 * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
148 *
149 * Allocate memory to hold a pagetable and allocate the IOMMU
150 * domain which is the actual IOMMU pagetable
151 * Return - void
152 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600153void *kgsl_iommu_create_pagetable(void)
154{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600155 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600156
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600157 iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
158 if (!iommu_pt) {
159 KGSL_CORE_ERR("kzalloc(%d) failed\n",
160 sizeof(struct kgsl_iommu_pt));
161 return NULL;
162 }
163 iommu_pt->domain = iommu_domain_alloc(0);
164 if (!iommu_pt->domain) {
165 KGSL_CORE_ERR("Failed to create iommu domain\n");
166 kfree(iommu_pt);
167 return NULL;
168 }
169 return iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600170}
171
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600172/*
173 * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
174 * pagetable
175 * @mmu - Pointer to the device mmu structure
176 * @priv - Flag indicating whether the private or user context is to be
177 * detached
178 *
179 * Detach the IOMMU unit with the domain that is contained in the
180 * hwpagetable of the given mmu. After detaching the IOMMU unit is not
181 * in use because the PTBR will not be set after a detach
182 * Return - void
183 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600184static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
185{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600186 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600187 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600188 int i, j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600189
190 BUG_ON(mmu->hwpagetable == NULL);
191 BUG_ON(mmu->hwpagetable->priv == NULL);
192
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600193 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600194
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600195 for (i = 0; i < iommu->unit_count; i++) {
196 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
197 for (j = 0; j < iommu_unit->dev_count; j++) {
198 if (iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600199 iommu_detach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600200 iommu_unit->dev[j].dev);
201 iommu_unit->dev[j].attached = false;
202 KGSL_MEM_INFO(mmu->device, "iommu %p detached "
203 "from user dev of MMU: %p\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600204 iommu_pt->domain, mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600205 }
206 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600207 }
208}
209
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600210/*
211 * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
212 * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
213 * setup other IOMMU registers for the device so that it becomes
214 * active
215 * @mmu - Pointer to the device mmu structure
216 * @priv - Flag indicating whether the private or user context is to be
217 * attached
218 *
219 * Attach the IOMMU unit with the domain that is contained in the
220 * hwpagetable of the given mmu.
221 * Return - 0 on success else error code
222 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600223static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
224{
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600225 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600226 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600227 int i, j, ret = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600228
229 BUG_ON(mmu->hwpagetable == NULL);
230 BUG_ON(mmu->hwpagetable->priv == NULL);
231
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600232 iommu_pt = mmu->hwpagetable->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600233
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600234 /*
235 * Loop through all the iommu devcies under all iommu units and
236 * attach the domain
237 */
238 for (i = 0; i < iommu->unit_count; i++) {
239 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
240 for (j = 0; j < iommu_unit->dev_count; j++) {
241 if (!iommu_unit->dev[j].attached) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600242 ret = iommu_attach_device(iommu_pt->domain,
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600243 iommu_unit->dev[j].dev);
244 if (ret) {
245 KGSL_MEM_ERR(mmu->device,
246 "Failed to attach device, err %d\n",
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700247 ret);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600248 goto done;
249 }
250 iommu_unit->dev[j].attached = true;
251 KGSL_MEM_INFO(mmu->device,
252 "iommu pt %p attached to dev %p, ctx_id %d\n",
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600253 iommu_pt->domain, iommu_unit->dev[j].dev,
254 iommu_unit->dev[j].ctx_id);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700255 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600256 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600257 }
258done:
259 return ret;
260}
261
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600262/*
263 * _get_iommu_ctxs - Get device pointer to IOMMU contexts
264 * @mmu - Pointer to mmu device
265 * data - Pointer to the platform data containing information about
266 * iommu devices for one iommu unit
267 * unit_id - The IOMMU unit number. This is not a specific ID but just
268 * a serial number. The serial numbers are treated as ID's of the
269 * IOMMU units
270 *
271 * Return - 0 on success else error code
272 */
273static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
274 struct kgsl_device_iommu_data *data, unsigned int unit_id)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700275{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600276 struct kgsl_iommu *iommu = mmu->priv;
277 struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700278 int i;
279
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600280 if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
281 KGSL_CORE_ERR("Too many iommu devices defined for an "
282 "IOMMU unit\n");
283 return -EINVAL;
284 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700285
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600286 for (i = 0; i < data->iommu_ctx_count; i++) {
287 if (!data->iommu_ctxs[i].iommu_ctx_name)
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700288 continue;
289
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600290 iommu_unit->dev[iommu_unit->dev_count].dev =
291 msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
292 if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
293 KGSL_CORE_ERR("Failed to get iommu dev handle for "
294 "device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700295 return -EINVAL;
296 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600297 if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
298 KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
299 KGSL_CORE_ERR("Invalid context ID defined: %d\n",
300 data->iommu_ctxs[i].ctx_id);
301 return -EINVAL;
302 }
303 iommu_unit->dev[iommu_unit->dev_count].ctx_id =
304 data->iommu_ctxs[i].ctx_id;
305 KGSL_DRV_INFO(mmu->device,
306 "Obtained dev handle %p for iommu context %s\n",
307 iommu_unit->dev[iommu_unit->dev_count].dev,
308 data->iommu_ctxs[i].iommu_ctx_name);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700309
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600310 iommu_unit->dev_count++;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700311 }
312
313 return 0;
314}
315
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600316/*
317 * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
318 * @mmu - Pointer to mmu device
319 *
320 * Get the device pointers for the IOMMU user and priv contexts of the
321 * kgsl device
322 * Return - 0 on success else error code
323 */
324static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600325{
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600326 struct platform_device *pdev =
327 container_of(mmu->device->parentdev, struct platform_device,
328 dev);
329 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
330 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700331 int i, ret = 0;
332
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600333 /* Go through the IOMMU data and get all the context devices */
334 if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
335 KGSL_CORE_ERR("Too many IOMMU units defined\n");
336 ret = -EINVAL;
337 goto done;
338 }
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700339
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600340 for (i = 0; i < pdata_dev->iommu_count; i++) {
341 ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700342 if (ret)
343 break;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600344 }
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600345 iommu->unit_count = pdata_dev->iommu_count;
346done:
Jordan Crouse46cf4bb2012-02-21 08:54:52 -0700347 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600348}
349
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600350/*
351 * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
352 * of the respective iommu units
353 * @mmu - Pointer to mmu structure
354 *
355 * Return - 0 on success else error code
356 */
357static int kgsl_set_register_map(struct kgsl_mmu *mmu)
358{
359 struct platform_device *pdev =
360 container_of(mmu->device->parentdev, struct platform_device,
361 dev);
362 struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
363 struct kgsl_iommu *iommu = mmu->device->mmu.priv;
364 struct kgsl_iommu_unit *iommu_unit;
365 int i = 0, ret = 0;
366
367 for (; i < pdata_dev->iommu_count; i++) {
368 struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
369 iommu_unit = &iommu->iommu_units[i];
370 /* set up the IOMMU register map for the given IOMMU unit */
371 if (!data.physstart || !data.physend) {
372 KGSL_CORE_ERR("The register range for IOMMU unit not"
373 " specified\n");
374 ret = -EINVAL;
375 goto err;
376 }
377 iommu_unit->reg_map.hostptr = ioremap(data.physstart,
378 data.physend - data.physstart + 1);
379 if (!iommu_unit->reg_map.hostptr) {
380 KGSL_CORE_ERR("Failed to map SMMU register address "
381 "space from %x to %x\n", data.physstart,
382 data.physend - data.physstart + 1);
383 ret = -ENOMEM;
384 i--;
385 goto err;
386 }
387 iommu_unit->reg_map.size = data.physend - data.physstart + 1;
388 iommu_unit->reg_map.physaddr = data.physstart;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600389 memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
390 iommu_unit->reg_map.size);
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600391 }
392 iommu->unit_count = pdata_dev->iommu_count;
393 return ret;
394err:
395 /* Unmap any mapped IOMMU regions */
396 for (; i >= 0; i--) {
397 iommu_unit = &iommu->iommu_units[i];
398 iounmap(iommu_unit->reg_map.hostptr);
399 iommu_unit->reg_map.size = 0;
400 iommu_unit->reg_map.physaddr = 0;
401 }
402 return ret;
403}
404
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600405static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600406 struct kgsl_pagetable *pagetable)
407{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600408 if (mmu->flags & KGSL_FLAGS_STARTED) {
409 /* page table not current, then setup mmu to use new
410 * specified page table
411 */
412 if (mmu->hwpagetable != pagetable) {
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600413 kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600414 kgsl_detach_pagetable_iommu_domain(mmu);
415 mmu->hwpagetable = pagetable;
416 if (mmu->hwpagetable)
417 kgsl_attach_pagetable_iommu_domain(mmu);
418 }
419 }
420}
421
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600422static int kgsl_iommu_init(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600423{
424 /*
425 * intialize device mmu
426 *
427 * call this with the global lock held
428 */
429 int status = 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600430 struct kgsl_iommu *iommu;
431
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600432 iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
433 if (!iommu) {
434 KGSL_CORE_ERR("kzalloc(%d) failed\n",
435 sizeof(struct kgsl_iommu));
436 return -ENOMEM;
437 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600438 iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
439 sizeof(unsigned long), GFP_KERNEL);
440 if (!iommu->asids) {
441 KGSL_CORE_ERR("kzalloc(%d) failed\n",
442 sizeof(struct kgsl_iommu));
443 status = -ENOMEM;
444 goto done;
445 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600446
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600447 mmu->priv = iommu;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600448 status = kgsl_get_iommu_ctxt(mmu);
449 if (status)
450 goto done;
Shubhraprakash Dase9eefd72012-05-01 01:44:59 -0600451 status = kgsl_set_register_map(mmu);
452 if (status)
453 goto done;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600454
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600455 dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600456 __func__);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600457done:
458 if (status) {
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600459 kfree(iommu->asids);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600460 kfree(iommu);
461 mmu->priv = NULL;
462 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600463 return status;
464}
465
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600466/*
467 * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
468 * for iommu. This function is only called once during first start, successive
469 * start do not call this funciton.
470 * @mmu - Pointer to mmu structure
471 *
472 * Create the initial defaultpagetable and setup the iommu mappings to it
473 * Return - 0 on success else error code
474 */
475static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
476{
477 int status = 0;
478 int i = 0;
479 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600480 struct kgsl_iommu_pt *iommu_pt;
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600481
482 mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
483 /* Return error if the default pagetable doesn't exist */
484 if (mmu->defaultpagetable == NULL) {
485 status = -ENOMEM;
486 goto err;
487 }
488 /* Map the IOMMU regsiters to only defaultpagetable */
489 for (i = 0; i < iommu->unit_count; i++) {
490 iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
491 status = kgsl_mmu_map(mmu->defaultpagetable,
492 &(iommu->iommu_units[i].reg_map),
493 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
494 if (status) {
495 iommu->iommu_units[i].reg_map.priv &=
496 ~KGSL_MEMFLAGS_GLOBAL;
497 goto err;
498 }
499 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600500 /*
501 * The dafault pagetable always has asid 0 assigned by the iommu driver
502 * and asid 1 is assigned to the private context.
503 */
504 iommu_pt = mmu->defaultpagetable->priv;
505 iommu_pt->asid = 0;
506 set_bit(0, iommu->asids);
507 set_bit(1, iommu->asids);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600508 return status;
509err:
510 for (i--; i >= 0; i--) {
511 kgsl_mmu_unmap(mmu->defaultpagetable,
512 &(iommu->iommu_units[i].reg_map));
513 iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
514 }
515 if (mmu->defaultpagetable) {
516 kgsl_mmu_putpagetable(mmu->defaultpagetable);
517 mmu->defaultpagetable = NULL;
518 }
519 return status;
520}
521
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600522static int kgsl_iommu_start(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600523{
524 int status;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600525 struct kgsl_iommu *iommu = mmu->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600526
527 if (mmu->flags & KGSL_FLAGS_STARTED)
528 return 0;
529
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600530 if (mmu->defaultpagetable == NULL) {
531 status = kgsl_iommu_setup_defaultpagetable(mmu);
532 if (status)
533 return -ENOMEM;
534 }
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600535 kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600536
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600537 mmu->hwpagetable = mmu->defaultpagetable;
538
539 status = kgsl_attach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600540 if (!status) {
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600541 mmu->flags |= KGSL_FLAGS_STARTED;
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600542 } else {
543 kgsl_detach_pagetable_iommu_domain(mmu);
544 mmu->hwpagetable = NULL;
545 }
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600546 status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
547 iommu->asid = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
548 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
549 KGSL_IOMMU_CONTEXTIDR);
550 kgsl_iommu_disable_clk(mmu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600551
552 return status;
553}
554
555static int
556kgsl_iommu_unmap(void *mmu_specific_pt,
557 struct kgsl_memdesc *memdesc)
558{
559 int ret;
560 unsigned int range = memdesc->size;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600561 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600562
563 /* All GPU addresses as assigned are page aligned, but some
564 functions purturb the gpuaddr with an offset, so apply the
565 mask here to make sure we have the right address */
566
567 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
568
569 if (range == 0 || gpuaddr == 0)
570 return 0;
571
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600572 ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600573 if (ret)
574 KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600575 "with err: %d\n", iommu_pt->domain, gpuaddr,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600576 range, ret);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600577
578 return 0;
579}
580
581static int
582kgsl_iommu_map(void *mmu_specific_pt,
583 struct kgsl_memdesc *memdesc,
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600584 unsigned int protflags,
585 unsigned int *tlb_flags)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600586{
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600587 int ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600588 unsigned int iommu_virt_addr;
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600589 struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600590
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600591 BUG_ON(NULL == iommu_pt);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600592
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600593
Jordan Croused17e9aa2011-10-12 16:57:48 -0600594 iommu_virt_addr = memdesc->gpuaddr;
595
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600596 ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
Olav Hauganf310cf22012-05-08 08:42:49 -0700597 memdesc->size, (IOMMU_READ | IOMMU_WRITE));
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600598 if (ret) {
599 KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600600 "failed with err: %d\n", iommu_pt->domain,
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600601 iommu_virt_addr, memdesc->sg, memdesc->size,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600602 (IOMMU_READ | IOMMU_WRITE), ret);
Shubhraprakash Das08894b92011-10-14 11:42:25 -0600603 return ret;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600604 }
605
Shubhraprakash Dasf764e462012-04-26 15:38:09 -0600606#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
607 /*
608 * Flushing only required if per process pagetables are used. With
609 * global case, flushing will happen inside iommu_map function
610 */
611 if (!ret)
612 *tlb_flags = UINT_MAX;
613#endif
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600614 return ret;
615}
616
Shubhraprakash Das79447952012-04-26 18:12:23 -0600617static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600618{
619 /*
620 * stop device mmu
621 *
622 * call this with the global lock held
623 */
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600624
625 if (mmu->flags & KGSL_FLAGS_STARTED) {
626 /* detach iommu attachment */
627 kgsl_detach_pagetable_iommu_domain(mmu);
Shubhraprakash Daseb6df1d2012-05-01 00:55:35 -0600628 mmu->hwpagetable = NULL;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600629
630 mmu->flags &= ~KGSL_FLAGS_STARTED;
631 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600632}
633
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600634static int kgsl_iommu_close(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600635{
Shubhraprakash Das589c7fe2012-05-04 17:30:20 -0600636 struct kgsl_iommu *iommu = mmu->priv;
637 int i;
638 for (i = 0; i < iommu->unit_count; i++) {
639 if (iommu->iommu_units[i].reg_map.gpuaddr)
640 kgsl_mmu_unmap(mmu->defaultpagetable,
641 &(iommu->iommu_units[i].reg_map));
642 if (iommu->iommu_units[i].reg_map.hostptr)
643 iounmap(iommu->iommu_units[i].reg_map.hostptr);
644 kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
645 iommu->iommu_units[i].reg_map.sglen);
646 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600647 if (mmu->defaultpagetable)
648 kgsl_mmu_putpagetable(mmu->defaultpagetable);
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600649 kfree(iommu->asids);
650 kfree(iommu);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600651
652 return 0;
653}
654
655static unsigned int
Shubhraprakash Das1c528262012-04-26 17:38:13 -0600656kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600657{
Shubhraprakash Das2b8716b2012-05-04 16:58:40 -0600658 unsigned int pt_base;
659 struct kgsl_iommu *iommu = mmu->priv;
660 /* Return the current pt base by reading IOMMU pt_base register */
661 kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
662 pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
663 (KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
664 KGSL_IOMMU_TTBR0);
665 kgsl_iommu_disable_clk(mmu);
666 return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
667 KGSL_IOMMU_TTBR0_PA_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600668}
669
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600670/*
671 * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
672 * pagetable
673 * @mmu - Pointer to mmu structure
674 *
675 * Allocates an asid to a IOMMU domain if it does not already have one. asid's
676 * are unique identifiers for pagetable that can be used to selectively flush
677 * tlb entries of the IOMMU unit.
678 * Return - asid to be used with the IOMMU domain
679 */
680static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
681{
682 struct kgsl_iommu *iommu = mmu->priv;
683 struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
684
685 /*
686 * If the iommu pagetable does not have any asid assigned and is not the
687 * default pagetable then assign asid.
688 */
689 if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
690 iommu_pt->asid = find_first_zero_bit(iommu->asids,
691 KGSL_IOMMU_MAX_ASIDS);
692 /* No free bits means reuse asid */
693 if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
694 iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
695 iommu->asid_reuse++;
696 }
697 set_bit(iommu_pt->asid, iommu->asids);
698 /*
699 * Store pointer to asids list so that during pagetable destroy
700 * the asid assigned to this pagetable may be cleared
701 */
702 iommu_pt->iommu = iommu;
703 }
704 /* Return the asid + the constant part of asid that never changes */
705 return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
706 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
707 (iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
708 KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
709}
710
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600711struct kgsl_mmu_ops iommu_ops = {
712 .mmu_init = kgsl_iommu_init,
713 .mmu_close = kgsl_iommu_close,
714 .mmu_start = kgsl_iommu_start,
715 .mmu_stop = kgsl_iommu_stop,
716 .mmu_setstate = kgsl_iommu_setstate,
717 .mmu_device_setstate = NULL,
718 .mmu_pagefault = NULL,
719 .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
Shubhraprakash Das9fb38ac2012-05-01 00:41:30 -0600720 .mmu_enable_clk = kgsl_iommu_enable_clk,
721 .mmu_disable_clk = kgsl_iommu_disable_clk,
Shubhraprakash Dasd3f937c2012-05-07 12:44:40 -0600722 .mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600723};
724
725struct kgsl_mmu_pt_ops iommu_pt_ops = {
726 .mmu_map = kgsl_iommu_map,
727 .mmu_unmap = kgsl_iommu_unmap,
728 .mmu_create_pagetable = kgsl_iommu_create_pagetable,
729 .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
730 .mmu_pt_equal = kgsl_iommu_pt_equal,
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600731};