blob: 2836a3ec559e58b7e90807946575f4e3dd9b3cec [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
2 *
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/sched.h>
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060019#include <linux/iommu.h>
Jordan Crouse817e0b92012-02-04 10:23:53 -070020#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021
22#include "kgsl.h"
23#include "kgsl_mmu.h"
24#include "kgsl_device.h"
25#include "kgsl_sharedmem.h"
Jeremy Gebbena3d07a42011-10-17 12:08:16 -060026#include "adreno_postmortem.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28#define KGSL_MMU_ALIGN_SHIFT 13
29#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
30
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060031static enum kgsl_mmutype kgsl_mmu_type;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable);
34
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035static int kgsl_cleanup_pt(struct kgsl_pagetable *pt)
36{
37 int i;
38 for (i = 0; i < KGSL_DEVICE_MAX; i++) {
39 struct kgsl_device *device = kgsl_driver.devp[i];
40 if (device)
41 device->ftbl->cleanup_pt(device, pt);
42 }
43 return 0;
44}
45
46static void kgsl_destroy_pagetable(struct kref *kref)
47{
48 struct kgsl_pagetable *pagetable = container_of(kref,
49 struct kgsl_pagetable, refcount);
50 unsigned long flags;
51
52 spin_lock_irqsave(&kgsl_driver.ptlock, flags);
53 list_del(&pagetable->list);
54 spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
55
56 pagetable_remove_sysfs_objects(pagetable);
57
58 kgsl_cleanup_pt(pagetable);
59
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060 if (pagetable->pool)
61 gen_pool_destroy(pagetable->pool);
62
Shubhraprakash Das767fdda2011-08-15 15:49:45 -060063 pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv);
64
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065 kfree(pagetable);
66}
67
68static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable)
69{
70 if (pagetable)
71 kref_put(&pagetable->refcount, kgsl_destroy_pagetable);
72}
73
74static struct kgsl_pagetable *
75kgsl_get_pagetable(unsigned long name)
76{
77 struct kgsl_pagetable *pt, *ret = NULL;
78 unsigned long flags;
79
80 spin_lock_irqsave(&kgsl_driver.ptlock, flags);
81 list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
82 if (pt->name == name) {
83 ret = pt;
84 kref_get(&ret->refcount);
85 break;
86 }
87 }
88
89 spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
90 return ret;
91}
92
93static struct kgsl_pagetable *
94_get_pt_from_kobj(struct kobject *kobj)
95{
96 unsigned long ptname;
97
98 if (!kobj)
99 return NULL;
100
101 if (sscanf(kobj->name, "%ld", &ptname) != 1)
102 return NULL;
103
104 return kgsl_get_pagetable(ptname);
105}
106
107static ssize_t
108sysfs_show_entries(struct kobject *kobj,
109 struct kobj_attribute *attr,
110 char *buf)
111{
112 struct kgsl_pagetable *pt;
113 int ret = 0;
114
115 pt = _get_pt_from_kobj(kobj);
116
117 if (pt)
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600118 ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.entries);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119
120 kgsl_put_pagetable(pt);
121 return ret;
122}
123
124static ssize_t
125sysfs_show_mapped(struct kobject *kobj,
126 struct kobj_attribute *attr,
127 char *buf)
128{
129 struct kgsl_pagetable *pt;
130 int ret = 0;
131
132 pt = _get_pt_from_kobj(kobj);
133
134 if (pt)
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600135 ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.mapped);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136
137 kgsl_put_pagetable(pt);
138 return ret;
139}
140
141static ssize_t
142sysfs_show_va_range(struct kobject *kobj,
143 struct kobj_attribute *attr,
144 char *buf)
145{
146 struct kgsl_pagetable *pt;
147 int ret = 0;
148
149 pt = _get_pt_from_kobj(kobj);
150
151 if (pt)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600152 ret += snprintf(buf, PAGE_SIZE, "0x%x\n",
153 CONFIG_MSM_KGSL_PAGE_TABLE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154
155 kgsl_put_pagetable(pt);
156 return ret;
157}
158
159static ssize_t
160sysfs_show_max_mapped(struct kobject *kobj,
161 struct kobj_attribute *attr,
162 char *buf)
163{
164 struct kgsl_pagetable *pt;
165 int ret = 0;
166
167 pt = _get_pt_from_kobj(kobj);
168
169 if (pt)
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600170 ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_mapped);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171
172 kgsl_put_pagetable(pt);
173 return ret;
174}
175
176static ssize_t
177sysfs_show_max_entries(struct kobject *kobj,
178 struct kobj_attribute *attr,
179 char *buf)
180{
181 struct kgsl_pagetable *pt;
182 int ret = 0;
183
184 pt = _get_pt_from_kobj(kobj);
185
186 if (pt)
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600187 ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_entries);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188
189 kgsl_put_pagetable(pt);
190 return ret;
191}
192
193static struct kobj_attribute attr_entries = {
194 .attr = { .name = "entries", .mode = 0444 },
195 .show = sysfs_show_entries,
196 .store = NULL,
197};
198
199static struct kobj_attribute attr_mapped = {
200 .attr = { .name = "mapped", .mode = 0444 },
201 .show = sysfs_show_mapped,
202 .store = NULL,
203};
204
205static struct kobj_attribute attr_va_range = {
206 .attr = { .name = "va_range", .mode = 0444 },
207 .show = sysfs_show_va_range,
208 .store = NULL,
209};
210
211static struct kobj_attribute attr_max_mapped = {
212 .attr = { .name = "max_mapped", .mode = 0444 },
213 .show = sysfs_show_max_mapped,
214 .store = NULL,
215};
216
217static struct kobj_attribute attr_max_entries = {
218 .attr = { .name = "max_entries", .mode = 0444 },
219 .show = sysfs_show_max_entries,
220 .store = NULL,
221};
222
223static struct attribute *pagetable_attrs[] = {
224 &attr_entries.attr,
225 &attr_mapped.attr,
226 &attr_va_range.attr,
227 &attr_max_mapped.attr,
228 &attr_max_entries.attr,
229 NULL,
230};
231
232static struct attribute_group pagetable_attr_group = {
233 .attrs = pagetable_attrs,
234};
235
236static void
237pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable)
238{
239 if (pagetable->kobj)
240 sysfs_remove_group(pagetable->kobj,
241 &pagetable_attr_group);
242
243 kobject_put(pagetable->kobj);
244}
245
246static int
247pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable)
248{
249 char ptname[16];
250 int ret = -ENOMEM;
251
252 snprintf(ptname, sizeof(ptname), "%d", pagetable->name);
253 pagetable->kobj = kobject_create_and_add(ptname,
254 kgsl_driver.ptkobj);
255 if (pagetable->kobj == NULL)
256 goto err;
257
258 ret = sysfs_create_group(pagetable->kobj, &pagetable_attr_group);
259
260err:
261 if (ret) {
262 if (pagetable->kobj)
263 kobject_put(pagetable->kobj);
264
265 pagetable->kobj = NULL;
266 }
267
268 return ret;
269}
270
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600271unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_device *device)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600273 struct kgsl_mmu *mmu = &device->mmu;
274 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
275 return 0;
276 else
277 return mmu->mmu_ops->mmu_get_current_ptbase(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600279EXPORT_SYMBOL(kgsl_mmu_get_current_ptbase);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280
Sushmita Susheelendra354d9712011-07-28 17:16:49 -0600281int
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600282kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base)
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600283{
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600284 struct kgsl_pagetable *pt;
285 int ptid = -1;
286
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600287 spin_lock(&kgsl_driver.ptlock);
288 list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600289 if (pt->pt_ops->mmu_pt_equal(pt, pt_base)) {
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600290 ptid = (int) pt->name;
291 break;
292 }
293 }
294 spin_unlock(&kgsl_driver.ptlock);
295
Sushmita Susheelendra354d9712011-07-28 17:16:49 -0600296 return ptid;
297}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600298EXPORT_SYMBOL(kgsl_mmu_get_ptname_from_ptbase);
Sushmita Susheelendra354d9712011-07-28 17:16:49 -0600299
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600300void kgsl_mmu_setstate(struct kgsl_device *device,
301 struct kgsl_pagetable *pagetable)
Sushmita Susheelendra354d9712011-07-28 17:16:49 -0600302{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600303 struct kgsl_mmu *mmu = &device->mmu;
Sushmita Susheelendra354d9712011-07-28 17:16:49 -0600304
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600305 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
306 return;
307 else
308 mmu->mmu_ops->mmu_setstate(device,
309 pagetable);
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600310}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600311EXPORT_SYMBOL(kgsl_mmu_setstate);
312
313int kgsl_mmu_init(struct kgsl_device *device)
314{
315 struct kgsl_mmu *mmu = &device->mmu;
316
317 mmu->device = device;
318
319 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) {
320 dev_info(device->dev, "|%s| MMU type set for device is "
321 "NOMMU\n", __func__);
322 return 0;
323 } else if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
324 mmu->mmu_ops = &gpummu_ops;
325 else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
326 mmu->mmu_ops = &iommu_ops;
327
328 return mmu->mmu_ops->mmu_init(device);
329}
330EXPORT_SYMBOL(kgsl_mmu_init);
331
332int kgsl_mmu_start(struct kgsl_device *device)
333{
334 struct kgsl_mmu *mmu = &device->mmu;
335
336 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
337 kgsl_regwrite(device, MH_MMU_CONFIG, 0);
338 return 0;
339 } else {
340 return mmu->mmu_ops->mmu_start(device);
341 }
342}
343EXPORT_SYMBOL(kgsl_mmu_start);
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600344
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345void kgsl_mh_intrcallback(struct kgsl_device *device)
346{
347 unsigned int status = 0;
348 unsigned int reg;
349
350 kgsl_regread(device, MH_INTERRUPT_STATUS, &status);
351 kgsl_regread(device, MH_AXI_ERROR, &reg);
352
353 if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR)
354 KGSL_MEM_CRIT(device, "axi read error interrupt: %08x\n", reg);
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600355 if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356 KGSL_MEM_CRIT(device, "axi write error interrupt: %08x\n", reg);
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600357 if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600358 device->mmu.mmu_ops->mmu_pagefault(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
Jordan Crousec8c9fcd2011-07-28 08:37:58 -0600360 status &= KGSL_MMU_INT_MASK;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361 kgsl_regwrite(device, MH_INTERRUPT_CLEAR, status);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362}
363EXPORT_SYMBOL(kgsl_mh_intrcallback);
364
365static int kgsl_setup_pt(struct kgsl_pagetable *pt)
366{
367 int i = 0;
368 int status = 0;
369
370 for (i = 0; i < KGSL_DEVICE_MAX; i++) {
371 struct kgsl_device *device = kgsl_driver.devp[i];
372 if (device) {
373 status = device->ftbl->setup_pt(device, pt);
374 if (status)
375 goto error_pt;
376 }
377 }
378 return status;
379error_pt:
380 while (i >= 0) {
381 struct kgsl_device *device = kgsl_driver.devp[i];
382 if (device)
383 device->ftbl->cleanup_pt(device, pt);
384 i--;
385 }
386 return status;
387}
388
389static struct kgsl_pagetable *kgsl_mmu_createpagetableobject(
390 unsigned int name)
391{
392 int status = 0;
393 struct kgsl_pagetable *pagetable = NULL;
394 unsigned long flags;
395
396 pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL);
397 if (pagetable == NULL) {
398 KGSL_CORE_ERR("kzalloc(%d) failed\n",
399 sizeof(struct kgsl_pagetable));
400 return NULL;
401 }
402
403 kref_init(&pagetable->refcount);
404
405 spin_lock_init(&pagetable->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 pagetable->name = name;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600407 pagetable->max_entries = KGSL_PAGETABLE_ENTRIES(
408 CONFIG_MSM_KGSL_PAGE_TABLE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409
410 pagetable->pool = gen_pool_create(PAGE_SHIFT, -1);
411 if (pagetable->pool == NULL) {
412 KGSL_CORE_ERR("gen_pool_create(%d) failed\n", PAGE_SHIFT);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600413 goto err_alloc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 }
415
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600416 if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE,
417 CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, -1)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418 KGSL_CORE_ERR("gen_pool_add failed\n");
419 goto err_pool;
420 }
421
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600422 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
423 pagetable->pt_ops = &gpummu_pt_ops;
424 else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
425 pagetable->pt_ops = &iommu_pt_ops;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600427 pagetable->priv = pagetable->pt_ops->mmu_create_pagetable();
428 if (!pagetable->priv)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 goto err_pool;
430
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 status = kgsl_setup_pt(pagetable);
432 if (status)
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600433 goto err_mmu_create;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434
435 spin_lock_irqsave(&kgsl_driver.ptlock, flags);
436 list_add(&pagetable->list, &kgsl_driver.pagetable_list);
437 spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
438
439 /* Create the sysfs entries */
440 pagetable_add_sysfs_objects(pagetable);
441
442 return pagetable;
443
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600444err_mmu_create:
445 pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446err_pool:
447 gen_pool_destroy(pagetable->pool);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448err_alloc:
449 kfree(pagetable);
450
451 return NULL;
452}
453
454struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name)
455{
456 struct kgsl_pagetable *pt;
457
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600458 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
459 return (void *)(-1);
460
461#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
462 if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
463 name = KGSL_MMU_GLOBAL_PT;
464#else
465 name = KGSL_MMU_GLOBAL_PT;
466#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 pt = kgsl_get_pagetable(name);
468
469 if (pt == NULL)
470 pt = kgsl_mmu_createpagetableobject(name);
471
472 return pt;
473}
474
475void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable)
476{
477 kgsl_put_pagetable(pagetable);
478}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600479EXPORT_SYMBOL(kgsl_mmu_putpagetable);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480
481void kgsl_setstate(struct kgsl_device *device, uint32_t flags)
482{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600483 struct kgsl_mmu *mmu = &device->mmu;
484 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
485 return;
486 else if (device->ftbl->setstate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 device->ftbl->setstate(device, flags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600488 else if (mmu->mmu_ops->mmu_device_setstate)
489 mmu->mmu_ops->mmu_device_setstate(device, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490}
491EXPORT_SYMBOL(kgsl_setstate);
492
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600493void kgsl_mmu_device_setstate(struct kgsl_device *device, uint32_t flags)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494{
495 struct kgsl_mmu *mmu = &device->mmu;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600496 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
497 return;
498 else if (mmu->mmu_ops->mmu_device_setstate)
499 mmu->mmu_ops->mmu_device_setstate(device, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600501EXPORT_SYMBOL(kgsl_mmu_device_setstate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502
Jeremy Gebben4e8aada2011-07-12 10:07:47 -0600503void kgsl_mh_start(struct kgsl_device *device)
504{
505 struct kgsl_mh *mh = &device->mh;
506 /* force mmu off to for now*/
507 kgsl_regwrite(device, MH_MMU_CONFIG, 0);
508 kgsl_idle(device, KGSL_TIMEOUT_DEFAULT);
509
510 /* define physical memory range accessible by the core */
511 kgsl_regwrite(device, MH_MMU_MPU_BASE, mh->mpu_base);
512 kgsl_regwrite(device, MH_MMU_MPU_END,
513 mh->mpu_base + mh->mpu_range);
514 kgsl_regwrite(device, MH_ARBITER_CONFIG, mh->mharb);
515
516 if (mh->mh_intf_cfg1 != 0)
517 kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG1,
518 mh->mh_intf_cfg1);
519
520 if (mh->mh_intf_cfg2 != 0)
521 kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG2,
522 mh->mh_intf_cfg2);
523
524 /*
525 * Interrupts are enabled on a per-device level when
526 * kgsl_pwrctrl_irq() is called
527 */
528}
529
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530int
531kgsl_mmu_map(struct kgsl_pagetable *pagetable,
532 struct kgsl_memdesc *memdesc,
533 unsigned int protflags)
534{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600535 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600537 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
538 memdesc->gpuaddr = memdesc->physaddr;
539 return 0;
540 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 memdesc->gpuaddr = gen_pool_alloc_aligned(pagetable->pool,
542 memdesc->size, KGSL_MMU_ALIGN_SHIFT);
543
544 if (memdesc->gpuaddr == 0) {
545 KGSL_CORE_ERR("gen_pool_alloc(%d) failed\n", memdesc->size);
546 KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
547 pagetable->name, pagetable->stats.mapped,
548 pagetable->stats.entries);
549 return -ENOMEM;
550 }
551
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 spin_lock(&pagetable->lock);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600553 ret = pagetable->pt_ops->mmu_map(pagetable->priv, memdesc, protflags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600555 if (ret)
556 goto err_free_gpuaddr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557
558 /* Keep track of the statistics for the sysfs files */
559
560 KGSL_STATS_ADD(1, pagetable->stats.entries,
561 pagetable->stats.max_entries);
562
563 KGSL_STATS_ADD(memdesc->size, pagetable->stats.mapped,
564 pagetable->stats.max_mapped);
565
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566 spin_unlock(&pagetable->lock);
567
568 return 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600569
570err_free_gpuaddr:
571 spin_unlock(&pagetable->lock);
572 gen_pool_free(pagetable->pool, memdesc->gpuaddr, memdesc->size);
573 memdesc->gpuaddr = 0;
574 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600576EXPORT_SYMBOL(kgsl_mmu_map);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577
578int
579kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
580 struct kgsl_memdesc *memdesc)
581{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600582 if (memdesc->size == 0 || memdesc->gpuaddr == 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 return 0;
584
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600585 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
586 memdesc->gpuaddr = 0;
587 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700588 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600589 spin_lock(&pagetable->lock);
590 pagetable->pt_ops->mmu_unmap(pagetable->priv, memdesc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591 /* Remove the statistics */
592 pagetable->stats.entries--;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600593 pagetable->stats.mapped -= memdesc->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594
595 spin_unlock(&pagetable->lock);
596
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600597 gen_pool_free(pagetable->pool,
598 memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK,
599 memdesc->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600
601 return 0;
602}
603EXPORT_SYMBOL(kgsl_mmu_unmap);
604
605int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable,
606 struct kgsl_memdesc *memdesc, unsigned int protflags)
607{
608 int result = -EINVAL;
609 unsigned int gpuaddr = 0;
610
611 if (memdesc == NULL) {
612 KGSL_CORE_ERR("invalid memdesc\n");
613 goto error;
614 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600615 /* Not all global mappings are needed for all MMU types */
616 if (!memdesc->size)
617 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618
619 gpuaddr = memdesc->gpuaddr;
620
621 result = kgsl_mmu_map(pagetable, memdesc, protflags);
622 if (result)
623 goto error;
624
625 /*global mappings must have the same gpu address in all pagetables*/
626 if (gpuaddr && gpuaddr != memdesc->gpuaddr) {
627 KGSL_CORE_ERR("pt %p addr mismatch phys 0x%08x"
628 "gpu 0x%0x 0x%08x", pagetable, memdesc->physaddr,
629 gpuaddr, memdesc->gpuaddr);
630 goto error_unmap;
631 }
632 return result;
633error_unmap:
634 kgsl_mmu_unmap(pagetable, memdesc);
635error:
636 return result;
637}
638EXPORT_SYMBOL(kgsl_mmu_map_global);
639
640int kgsl_mmu_stop(struct kgsl_device *device)
641{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 struct kgsl_mmu *mmu = &device->mmu;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600643
644 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
645 return 0;
646 else
647 return mmu->mmu_ops->mmu_stop(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648}
649EXPORT_SYMBOL(kgsl_mmu_stop);
650
651int kgsl_mmu_close(struct kgsl_device *device)
652{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653 struct kgsl_mmu *mmu = &device->mmu;
654
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600655 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
656 return 0;
657 else
658 return mmu->mmu_ops->mmu_close(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600660EXPORT_SYMBOL(kgsl_mmu_close);
661
662int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt,
663 enum kgsl_deviceid id)
664{
665 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
666 return pt->pt_ops->mmu_pt_get_flags(pt, id);
667 else
668 return 0;
669}
670EXPORT_SYMBOL(kgsl_mmu_pt_get_flags);
671
672void kgsl_mmu_ptpool_destroy(void *ptpool)
673{
674 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
675 kgsl_gpummu_ptpool_destroy(ptpool);
676 ptpool = 0;
677}
678EXPORT_SYMBOL(kgsl_mmu_ptpool_destroy);
679
680void *kgsl_mmu_ptpool_init(int ptsize, int entries)
681{
682 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
683 return kgsl_gpummu_ptpool_init(ptsize, entries);
684 else
685 return (void *)(-1);
686}
687EXPORT_SYMBOL(kgsl_mmu_ptpool_init);
688
689int kgsl_mmu_enabled(void)
690{
691 if (KGSL_MMU_TYPE_NONE != kgsl_mmu_type)
692 return 1;
693 else
694 return 0;
695}
696EXPORT_SYMBOL(kgsl_mmu_enabled);
697
698int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt,
699 unsigned int pt_base)
700{
701 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
702 return true;
703 else
704 return pt->pt_ops->mmu_pt_equal(pt, pt_base);
705}
706EXPORT_SYMBOL(kgsl_mmu_pt_equal);
707
708enum kgsl_mmutype kgsl_mmu_get_mmutype(void)
709{
710 return kgsl_mmu_type;
711}
712EXPORT_SYMBOL(kgsl_mmu_get_mmutype);
713
714void kgsl_mmu_set_mmutype(char *mmutype)
715{
Jordan Crouse817e0b92012-02-04 10:23:53 -0700716 /* Set the default MMU - GPU on <=8960 and nothing on >= 8064 */
717 kgsl_mmu_type =
718 cpu_is_apq8064() ? KGSL_MMU_TYPE_NONE : KGSL_MMU_TYPE_GPU;
719
720 /* Use the IOMMU if it is found */
721 if (iommu_found())
722 kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
723
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600724 if (mmutype && !strncmp(mmutype, "gpummu", 6))
725 kgsl_mmu_type = KGSL_MMU_TYPE_GPU;
726 if (iommu_found() && mmutype && !strncmp(mmutype, "iommu", 5))
727 kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
728 if (mmutype && !strncmp(mmutype, "nommu", 5))
729 kgsl_mmu_type = KGSL_MMU_TYPE_NONE;
730}
731EXPORT_SYMBOL(kgsl_mmu_set_mmutype);