blob: 671479ea9c0b4f6352e4780ca5f6ff5924e786cb [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) {
Jordan Crouse40861a42012-02-06 10:18:23 -0700538 if (memdesc->sglen == 1) {
539 memdesc->gpuaddr = sg_phys(memdesc->sg);
540 return 0;
541 } else {
542 KGSL_CORE_ERR("Memory is not contigious "
543 "(sglen = %d)\n", memdesc->sglen);
544 return -EINVAL;
545 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600546 }
Jordan Crouse40861a42012-02-06 10:18:23 -0700547
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 memdesc->gpuaddr = gen_pool_alloc_aligned(pagetable->pool,
549 memdesc->size, KGSL_MMU_ALIGN_SHIFT);
550
551 if (memdesc->gpuaddr == 0) {
552 KGSL_CORE_ERR("gen_pool_alloc(%d) failed\n", memdesc->size);
553 KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
554 pagetable->name, pagetable->stats.mapped,
555 pagetable->stats.entries);
556 return -ENOMEM;
557 }
558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559 spin_lock(&pagetable->lock);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600560 ret = pagetable->pt_ops->mmu_map(pagetable->priv, memdesc, protflags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600562 if (ret)
563 goto err_free_gpuaddr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564
565 /* Keep track of the statistics for the sysfs files */
566
567 KGSL_STATS_ADD(1, pagetable->stats.entries,
568 pagetable->stats.max_entries);
569
570 KGSL_STATS_ADD(memdesc->size, pagetable->stats.mapped,
571 pagetable->stats.max_mapped);
572
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 spin_unlock(&pagetable->lock);
574
575 return 0;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600576
577err_free_gpuaddr:
578 spin_unlock(&pagetable->lock);
579 gen_pool_free(pagetable->pool, memdesc->gpuaddr, memdesc->size);
580 memdesc->gpuaddr = 0;
581 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600583EXPORT_SYMBOL(kgsl_mmu_map);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700584
585int
586kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
587 struct kgsl_memdesc *memdesc)
588{
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600589 if (memdesc->size == 0 || memdesc->gpuaddr == 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590 return 0;
591
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600592 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
593 memdesc->gpuaddr = 0;
594 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600596 spin_lock(&pagetable->lock);
597 pagetable->pt_ops->mmu_unmap(pagetable->priv, memdesc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 /* Remove the statistics */
599 pagetable->stats.entries--;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600600 pagetable->stats.mapped -= memdesc->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601
602 spin_unlock(&pagetable->lock);
603
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600604 gen_pool_free(pagetable->pool,
605 memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK,
606 memdesc->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607
608 return 0;
609}
610EXPORT_SYMBOL(kgsl_mmu_unmap);
611
612int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable,
613 struct kgsl_memdesc *memdesc, unsigned int protflags)
614{
615 int result = -EINVAL;
616 unsigned int gpuaddr = 0;
617
618 if (memdesc == NULL) {
619 KGSL_CORE_ERR("invalid memdesc\n");
620 goto error;
621 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600622 /* Not all global mappings are needed for all MMU types */
623 if (!memdesc->size)
624 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625
626 gpuaddr = memdesc->gpuaddr;
627
628 result = kgsl_mmu_map(pagetable, memdesc, protflags);
629 if (result)
630 goto error;
631
632 /*global mappings must have the same gpu address in all pagetables*/
633 if (gpuaddr && gpuaddr != memdesc->gpuaddr) {
634 KGSL_CORE_ERR("pt %p addr mismatch phys 0x%08x"
635 "gpu 0x%0x 0x%08x", pagetable, memdesc->physaddr,
636 gpuaddr, memdesc->gpuaddr);
637 goto error_unmap;
638 }
639 return result;
640error_unmap:
641 kgsl_mmu_unmap(pagetable, memdesc);
642error:
643 return result;
644}
645EXPORT_SYMBOL(kgsl_mmu_map_global);
646
647int kgsl_mmu_stop(struct kgsl_device *device)
648{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 struct kgsl_mmu *mmu = &device->mmu;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600650
651 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
652 return 0;
653 else
654 return mmu->mmu_ops->mmu_stop(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655}
656EXPORT_SYMBOL(kgsl_mmu_stop);
657
658int kgsl_mmu_close(struct kgsl_device *device)
659{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660 struct kgsl_mmu *mmu = &device->mmu;
661
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600662 if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
663 return 0;
664 else
665 return mmu->mmu_ops->mmu_close(device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666}
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600667EXPORT_SYMBOL(kgsl_mmu_close);
668
669int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt,
670 enum kgsl_deviceid id)
671{
672 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
673 return pt->pt_ops->mmu_pt_get_flags(pt, id);
674 else
675 return 0;
676}
677EXPORT_SYMBOL(kgsl_mmu_pt_get_flags);
678
679void kgsl_mmu_ptpool_destroy(void *ptpool)
680{
681 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
682 kgsl_gpummu_ptpool_destroy(ptpool);
683 ptpool = 0;
684}
685EXPORT_SYMBOL(kgsl_mmu_ptpool_destroy);
686
687void *kgsl_mmu_ptpool_init(int ptsize, int entries)
688{
689 if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
690 return kgsl_gpummu_ptpool_init(ptsize, entries);
691 else
692 return (void *)(-1);
693}
694EXPORT_SYMBOL(kgsl_mmu_ptpool_init);
695
696int kgsl_mmu_enabled(void)
697{
698 if (KGSL_MMU_TYPE_NONE != kgsl_mmu_type)
699 return 1;
700 else
701 return 0;
702}
703EXPORT_SYMBOL(kgsl_mmu_enabled);
704
705int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt,
706 unsigned int pt_base)
707{
708 if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
709 return true;
710 else
711 return pt->pt_ops->mmu_pt_equal(pt, pt_base);
712}
713EXPORT_SYMBOL(kgsl_mmu_pt_equal);
714
715enum kgsl_mmutype kgsl_mmu_get_mmutype(void)
716{
717 return kgsl_mmu_type;
718}
719EXPORT_SYMBOL(kgsl_mmu_get_mmutype);
720
721void kgsl_mmu_set_mmutype(char *mmutype)
722{
Jordan Crouse817e0b92012-02-04 10:23:53 -0700723 /* Set the default MMU - GPU on <=8960 and nothing on >= 8064 */
724 kgsl_mmu_type =
725 cpu_is_apq8064() ? KGSL_MMU_TYPE_NONE : KGSL_MMU_TYPE_GPU;
726
727 /* Use the IOMMU if it is found */
728 if (iommu_found())
729 kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
730
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600731 if (mmutype && !strncmp(mmutype, "gpummu", 6))
732 kgsl_mmu_type = KGSL_MMU_TYPE_GPU;
733 if (iommu_found() && mmutype && !strncmp(mmutype, "iommu", 5))
734 kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
735 if (mmutype && !strncmp(mmutype, "nommu", 5))
736 kgsl_mmu_type = KGSL_MMU_TYPE_NONE;
737}
738EXPORT_SYMBOL(kgsl_mmu_set_mmutype);