blob: 97b5ef14c173c0b93b599daa8272cf607b67550e [file] [log] [blame]
Shubhraprakash Dasc0f21b62012-02-16 11:24:43 -07001/* 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/sched.h>
19
20#include "kgsl.h"
21#include "kgsl_mmu.h"
22#include "kgsl_device.h"
23#include "kgsl_sharedmem.h"
24
25static ssize_t
26sysfs_show_ptpool_entries(struct kobject *kobj,
27 struct kobj_attribute *attr,
28 char *buf)
29{
30 struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
31 kgsl_driver.ptpool;
32 return snprintf(buf, PAGE_SIZE, "%d\n", pool->entries);
33}
34
35static ssize_t
36sysfs_show_ptpool_min(struct kobject *kobj,
37 struct kobj_attribute *attr,
38 char *buf)
39{
40 struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
41 kgsl_driver.ptpool;
42 return snprintf(buf, PAGE_SIZE, "%d\n",
43 pool->static_entries);
44}
45
46static ssize_t
47sysfs_show_ptpool_chunks(struct kobject *kobj,
48 struct kobj_attribute *attr,
49 char *buf)
50{
51 struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
52 kgsl_driver.ptpool;
53 return snprintf(buf, PAGE_SIZE, "%d\n", pool->chunks);
54}
55
56static ssize_t
57sysfs_show_ptpool_ptsize(struct kobject *kobj,
58 struct kobj_attribute *attr,
59 char *buf)
60{
61 struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
62 kgsl_driver.ptpool;
63 return snprintf(buf, PAGE_SIZE, "%d\n", pool->ptsize);
64}
65
66static struct kobj_attribute attr_ptpool_entries = {
67 .attr = { .name = "ptpool_entries", .mode = 0444 },
68 .show = sysfs_show_ptpool_entries,
69 .store = NULL,
70};
71
72static struct kobj_attribute attr_ptpool_min = {
73 .attr = { .name = "ptpool_min", .mode = 0444 },
74 .show = sysfs_show_ptpool_min,
75 .store = NULL,
76};
77
78static struct kobj_attribute attr_ptpool_chunks = {
79 .attr = { .name = "ptpool_chunks", .mode = 0444 },
80 .show = sysfs_show_ptpool_chunks,
81 .store = NULL,
82};
83
84static struct kobj_attribute attr_ptpool_ptsize = {
85 .attr = { .name = "ptpool_ptsize", .mode = 0444 },
86 .show = sysfs_show_ptpool_ptsize,
87 .store = NULL,
88};
89
90static struct attribute *ptpool_attrs[] = {
91 &attr_ptpool_entries.attr,
92 &attr_ptpool_min.attr,
93 &attr_ptpool_chunks.attr,
94 &attr_ptpool_ptsize.attr,
95 NULL,
96};
97
98static struct attribute_group ptpool_attr_group = {
99 .attrs = ptpool_attrs,
100};
101
102static int
103_kgsl_ptpool_add_entries(struct kgsl_ptpool *pool, int count, int dynamic)
104{
105 struct kgsl_ptpool_chunk *chunk;
106 size_t size = ALIGN(count * pool->ptsize, PAGE_SIZE);
107
108 BUG_ON(count == 0);
109
110 if (get_order(size) >= MAX_ORDER) {
111 KGSL_CORE_ERR("ptpool allocation is too big: %d\n", size);
112 return -EINVAL;
113 }
114
115 chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
116 if (chunk == NULL) {
117 KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*chunk));
118 return -ENOMEM;
119 }
120
121 chunk->size = size;
122 chunk->count = count;
123 chunk->dynamic = dynamic;
124
125 chunk->data = dma_alloc_coherent(NULL, size,
126 &chunk->phys, GFP_KERNEL);
127
128 if (chunk->data == NULL) {
129 KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size);
130 goto err;
131 }
132
133 chunk->bitmap = kzalloc(BITS_TO_LONGS(count) * 4, GFP_KERNEL);
134
135 if (chunk->bitmap == NULL) {
136 KGSL_CORE_ERR("kzalloc(%d) failed\n",
137 BITS_TO_LONGS(count) * 4);
138 goto err_dma;
139 }
140
141 list_add_tail(&chunk->list, &pool->list);
142
143 pool->chunks++;
144 pool->entries += count;
145
146 if (!dynamic)
147 pool->static_entries += count;
148
149 return 0;
150
151err_dma:
152 dma_free_coherent(NULL, chunk->size, chunk->data, chunk->phys);
153err:
154 kfree(chunk);
155 return -ENOMEM;
156}
157
158static void *
159_kgsl_ptpool_get_entry(struct kgsl_ptpool *pool, unsigned int *physaddr)
160{
161 struct kgsl_ptpool_chunk *chunk;
162
163 list_for_each_entry(chunk, &pool->list, list) {
164 int bit = find_first_zero_bit(chunk->bitmap, chunk->count);
165
166 if (bit >= chunk->count)
167 continue;
168
169 set_bit(bit, chunk->bitmap);
170 *physaddr = chunk->phys + (bit * pool->ptsize);
171
172 return chunk->data + (bit * pool->ptsize);
173 }
174
175 return NULL;
176}
177
178/**
179 * kgsl_ptpool_add
180 * @pool: A pointer to a ptpool structure
181 * @entries: Number of entries to add
182 *
183 * Add static entries to the pagetable pool.
184 */
185
186static int
187kgsl_ptpool_add(struct kgsl_ptpool *pool, int count)
188{
189 int ret = 0;
190 BUG_ON(count == 0);
191
192 mutex_lock(&pool->lock);
193
194 /* Only 4MB can be allocated in one chunk, so larger allocations
195 need to be split into multiple sections */
196
197 while (count) {
198 int entries = ((count * pool->ptsize) > SZ_4M) ?
199 SZ_4M / pool->ptsize : count;
200
201 /* Add the entries as static, i.e. they don't ever stand
202 a chance of being removed */
203
204 ret = _kgsl_ptpool_add_entries(pool, entries, 0);
205 if (ret)
206 break;
207
208 count -= entries;
209 }
210
211 mutex_unlock(&pool->lock);
212 return ret;
213}
214
215/**
216 * kgsl_ptpool_alloc
217 * @pool: A pointer to a ptpool structure
218 * @addr: A pointer to store the physical address of the chunk
219 *
220 * Allocate a pagetable from the pool. Returns the virtual address
221 * of the pagetable, the physical address is returned in physaddr
222 */
223
224static void *kgsl_ptpool_alloc(struct kgsl_ptpool *pool,
225 unsigned int *physaddr)
226{
227 void *addr = NULL;
228 int ret;
229
230 mutex_lock(&pool->lock);
231 addr = _kgsl_ptpool_get_entry(pool, physaddr);
232 if (addr)
233 goto done;
234
235 /* Add a chunk for 1 more pagetable and mark it as dynamic */
236 ret = _kgsl_ptpool_add_entries(pool, 1, 1);
237
238 if (ret)
239 goto done;
240
241 addr = _kgsl_ptpool_get_entry(pool, physaddr);
242done:
243 mutex_unlock(&pool->lock);
244 return addr;
245}
246
247static inline void _kgsl_ptpool_rm_chunk(struct kgsl_ptpool_chunk *chunk)
248{
249 list_del(&chunk->list);
250
251 if (chunk->data)
252 dma_free_coherent(NULL, chunk->size, chunk->data,
253 chunk->phys);
254 kfree(chunk->bitmap);
255 kfree(chunk);
256}
257
258/**
259 * kgsl_ptpool_free
260 * @pool: A pointer to a ptpool structure
261 * @addr: A pointer to the virtual address to free
262 *
263 * Free a pagetable allocated from the pool
264 */
265
266static void kgsl_ptpool_free(struct kgsl_ptpool *pool, void *addr)
267{
268 struct kgsl_ptpool_chunk *chunk, *tmp;
269
270 if (pool == NULL || addr == NULL)
271 return;
272
273 mutex_lock(&pool->lock);
274 list_for_each_entry_safe(chunk, tmp, &pool->list, list) {
275 if (addr >= chunk->data &&
276 addr < chunk->data + chunk->size) {
277 int bit = ((unsigned long) (addr - chunk->data)) /
278 pool->ptsize;
279
280 clear_bit(bit, chunk->bitmap);
281 memset(addr, 0, pool->ptsize);
282
283 if (chunk->dynamic &&
284 bitmap_empty(chunk->bitmap, chunk->count))
285 _kgsl_ptpool_rm_chunk(chunk);
286
287 break;
288 }
289 }
290
291 mutex_unlock(&pool->lock);
292}
293
294void kgsl_gpummu_ptpool_destroy(void *ptpool)
295{
296 struct kgsl_ptpool *pool = (struct kgsl_ptpool *)ptpool;
297 struct kgsl_ptpool_chunk *chunk, *tmp;
298
299 if (pool == NULL)
300 return;
301
302 mutex_lock(&pool->lock);
303 list_for_each_entry_safe(chunk, tmp, &pool->list, list)
304 _kgsl_ptpool_rm_chunk(chunk);
305 mutex_unlock(&pool->lock);
306
307 kfree(pool);
308}
309
310/**
311 * kgsl_ptpool_init
312 * @pool: A pointer to a ptpool structure to initialize
313 * @ptsize: The size of each pagetable entry
314 * @entries: The number of inital entries to add to the pool
315 *
316 * Initalize a pool and allocate an initial chunk of entries.
317 */
318void *kgsl_gpummu_ptpool_init(int ptsize, int entries)
319{
320 struct kgsl_ptpool *pool;
321 int ret = 0;
322 BUG_ON(ptsize == 0);
323
324 pool = kzalloc(sizeof(struct kgsl_ptpool), GFP_KERNEL);
325 if (!pool) {
326 KGSL_CORE_ERR("Failed to allocate memory "
327 "for ptpool\n");
328 return NULL;
329 }
330
331 pool->ptsize = ptsize;
332 mutex_init(&pool->lock);
333 INIT_LIST_HEAD(&pool->list);
334
335 if (entries) {
336 ret = kgsl_ptpool_add(pool, entries);
337 if (ret)
338 goto err_ptpool_remove;
339 }
340
341 ret = sysfs_create_group(kgsl_driver.ptkobj, &ptpool_attr_group);
342 if (ret) {
343 KGSL_CORE_ERR("sysfs_create_group failed for ptpool "
344 "statistics: %d\n", ret);
345 goto err_ptpool_remove;
346 }
347 return (void *)pool;
348
349err_ptpool_remove:
350 kgsl_gpummu_ptpool_destroy(pool);
351 return NULL;
352}
353
354int kgsl_gpummu_pt_equal(struct kgsl_pagetable *pt,
355 unsigned int pt_base)
356{
357 struct kgsl_gpummu_pt *gpummu_pt = pt->priv;
358 return pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base);
359}
360
361void kgsl_gpummu_destroy_pagetable(void *mmu_specific_pt)
362{
363 struct kgsl_gpummu_pt *gpummu_pt = (struct kgsl_gpummu_pt *)
364 mmu_specific_pt;
365 kgsl_ptpool_free((struct kgsl_ptpool *)kgsl_driver.ptpool,
366 gpummu_pt->base.hostptr);
367
368 kgsl_driver.stats.coherent -= KGSL_PAGETABLE_SIZE;
369
370 kfree(gpummu_pt->tlbflushfilter.base);
371
372 kfree(gpummu_pt);
373}
374
375static inline uint32_t
376kgsl_pt_entry_get(unsigned int va_base, uint32_t va)
377{
378 return (va - va_base) >> PAGE_SHIFT;
379}
380
381static inline void
382kgsl_pt_map_set(struct kgsl_gpummu_pt *pt, uint32_t pte, uint32_t val)
383{
384 uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700385 BUG_ON(pte*sizeof(uint32_t) >= pt->base.size);
386 baseptr[pte] = val;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600387}
388
389static inline uint32_t
390kgsl_pt_map_get(struct kgsl_gpummu_pt *pt, uint32_t pte)
391{
392 uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700393 BUG_ON(pte*sizeof(uint32_t) >= pt->base.size);
394 return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600395}
396
397static unsigned int kgsl_gpummu_pt_get_flags(struct kgsl_pagetable *pt,
398 enum kgsl_deviceid id)
399{
400 unsigned int result = 0;
Shubhraprakash Dasc0f21b62012-02-16 11:24:43 -0700401 struct kgsl_gpummu_pt *gpummu_pt;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600402
403 if (pt == NULL)
404 return 0;
Shubhraprakash Dasc0f21b62012-02-16 11:24:43 -0700405 gpummu_pt = pt->priv;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600406
407 spin_lock(&pt->lock);
408 if (gpummu_pt->tlb_flags && (1<<id)) {
409 result = KGSL_MMUFLAGS_TLBFLUSH;
410 gpummu_pt->tlb_flags &= ~(1<<id);
411 }
412 spin_unlock(&pt->lock);
413 return result;
414}
415
416static void kgsl_gpummu_pagefault(struct kgsl_device *device)
417{
418 unsigned int reg;
419 unsigned int ptbase;
420
421 kgsl_regread(device, MH_MMU_PAGE_FAULT, &reg);
422 kgsl_regread(device, MH_MMU_PT_BASE, &ptbase);
423
424 KGSL_MEM_CRIT(device,
425 "mmu page fault: page=0x%lx pt=%d op=%s axi=%d\n",
426 reg & ~(PAGE_SIZE - 1),
427 kgsl_mmu_get_ptname_from_ptbase(ptbase),
428 reg & 0x02 ? "WRITE" : "READ", (reg >> 4) & 0xF);
429}
430
431static void *kgsl_gpummu_create_pagetable(void)
432{
433 struct kgsl_gpummu_pt *gpummu_pt;
434
435 gpummu_pt = kzalloc(sizeof(struct kgsl_gpummu_pt),
436 GFP_KERNEL);
437 if (!gpummu_pt)
438 return NULL;
439
440 gpummu_pt->tlb_flags = 0;
441 gpummu_pt->last_superpte = 0;
442
443 gpummu_pt->tlbflushfilter.size = (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE /
444 (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1;
445 gpummu_pt->tlbflushfilter.base = (unsigned int *)
446 kzalloc(gpummu_pt->tlbflushfilter.size, GFP_KERNEL);
447 if (!gpummu_pt->tlbflushfilter.base) {
448 KGSL_CORE_ERR("kzalloc(%d) failed\n",
449 gpummu_pt->tlbflushfilter.size);
450 goto err_free_gpummu;
451 }
452 GSL_TLBFLUSH_FILTER_RESET();
453
454 gpummu_pt->base.hostptr = kgsl_ptpool_alloc((struct kgsl_ptpool *)
455 kgsl_driver.ptpool,
456 &gpummu_pt->base.physaddr);
457
458 if (gpummu_pt->base.hostptr == NULL)
459 goto err_flushfilter;
460
461 /* ptpool allocations are from coherent memory, so update the
462 device statistics acordingly */
463
464 KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent,
465 kgsl_driver.stats.coherent_max);
466
467 gpummu_pt->base.gpuaddr = gpummu_pt->base.physaddr;
468 gpummu_pt->base.size = KGSL_PAGETABLE_SIZE;
469
470 return (void *)gpummu_pt;
471
472err_flushfilter:
473 kfree(gpummu_pt->tlbflushfilter.base);
474err_free_gpummu:
475 kfree(gpummu_pt);
476
477 return NULL;
478}
479
480static void kgsl_gpummu_default_setstate(struct kgsl_device *device,
481 uint32_t flags)
482{
483 struct kgsl_gpummu_pt *gpummu_pt;
484 if (!kgsl_mmu_enabled())
485 return;
486
487 if (flags & KGSL_MMUFLAGS_PTUPDATE) {
488 kgsl_idle(device, KGSL_TIMEOUT_DEFAULT);
489 gpummu_pt = device->mmu.hwpagetable->priv;
490 kgsl_regwrite(device, MH_MMU_PT_BASE,
491 gpummu_pt->base.gpuaddr);
492 }
493
494 if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
495 /* Invalidate all and tc */
496 kgsl_regwrite(device, MH_MMU_INVALIDATE, 0x00000003);
497 }
498}
499
500static void kgsl_gpummu_setstate(struct kgsl_device *device,
501 struct kgsl_pagetable *pagetable)
502{
503 struct kgsl_mmu *mmu = &device->mmu;
504 struct kgsl_gpummu_pt *gpummu_pt;
505
506 if (mmu->flags & KGSL_FLAGS_STARTED) {
507 /* page table not current, then setup mmu to use new
508 * specified page table
509 */
510 if (mmu->hwpagetable != pagetable) {
511 mmu->hwpagetable = pagetable;
512 spin_lock(&mmu->hwpagetable->lock);
513 gpummu_pt = mmu->hwpagetable->priv;
514 gpummu_pt->tlb_flags &= ~(1<<device->id);
515 spin_unlock(&mmu->hwpagetable->lock);
516
517 /* call device specific set page table */
518 kgsl_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH |
519 KGSL_MMUFLAGS_PTUPDATE);
520 }
521 }
522}
523
524static int kgsl_gpummu_init(struct kgsl_device *device)
525{
526 /*
527 * intialize device mmu
528 *
529 * call this with the global lock held
530 */
531 int status = 0;
532 struct kgsl_mmu *mmu = &device->mmu;
533
534 mmu->device = device;
535
536 /* sub-client MMU lookups require address translation */
537 if ((mmu->config & ~0x1) > 0) {
538 /*make sure virtual address range is a multiple of 64Kb */
539 if (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE & ((1 << 16) - 1)) {
540 KGSL_CORE_ERR("Invalid pagetable size requested "
541 "for GPUMMU: %x\n", CONFIG_MSM_KGSL_PAGE_TABLE_SIZE);
542 return -EINVAL;
543 }
544
545 /* allocate memory used for completing r/w operations that
546 * cannot be mapped by the MMU
547 */
548 status = kgsl_allocate_contiguous(&mmu->setstate_memory, 64);
549 if (!status)
550 kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
551 mmu->setstate_memory.size);
552 }
553
554 dev_info(device->dev, "|%s| MMU type set for device is GPUMMU\n",
555 __func__);
556 return status;
557}
558
559static int kgsl_gpummu_start(struct kgsl_device *device)
560{
561 /*
562 * intialize device mmu
563 *
564 * call this with the global lock held
565 */
566
567 struct kgsl_mmu *mmu = &device->mmu;
568 struct kgsl_gpummu_pt *gpummu_pt;
569
570 if (mmu->flags & KGSL_FLAGS_STARTED)
571 return 0;
572
573 /* MMU not enabled */
574 if ((mmu->config & 0x1) == 0)
575 return 0;
576
577 /* setup MMU and sub-client behavior */
578 kgsl_regwrite(device, MH_MMU_CONFIG, mmu->config);
579
580 /* idle device */
581 kgsl_idle(device, KGSL_TIMEOUT_DEFAULT);
582
583 /* enable axi interrupts */
584 kgsl_regwrite(device, MH_INTERRUPT_MASK,
585 GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT);
586
587 kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
588 mmu->setstate_memory.size);
589
590 /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory
591 * to complete transactions in case of an MMU fault. Note that
592 * we'll leave the bottom 32 bytes of the setstate_memory for other
593 * purposes (e.g. use it when dummy read cycles are needed
594 * for other blocks) */
595 kgsl_regwrite(device, MH_MMU_TRAN_ERROR,
596 mmu->setstate_memory.physaddr + 32);
597
598 if (mmu->defaultpagetable == NULL)
599 mmu->defaultpagetable =
600 kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
601
602 /* Return error if the default pagetable doesn't exist */
603 if (mmu->defaultpagetable == NULL)
604 return -ENOMEM;
605
606 mmu->hwpagetable = mmu->defaultpagetable;
607 gpummu_pt = mmu->hwpagetable->priv;
608 kgsl_regwrite(device, MH_MMU_PT_BASE,
609 gpummu_pt->base.gpuaddr);
610 kgsl_regwrite(device, MH_MMU_VA_RANGE,
611 (KGSL_PAGETABLE_BASE |
612 (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE >> 16)));
613 kgsl_setstate(device, KGSL_MMUFLAGS_TLBFLUSH);
614 mmu->flags |= KGSL_FLAGS_STARTED;
615
616 return 0;
617}
618
619static int
620kgsl_gpummu_unmap(void *mmu_specific_pt,
621 struct kgsl_memdesc *memdesc)
622{
623 unsigned int numpages;
624 unsigned int pte, ptefirst, ptelast, superpte;
625 unsigned int range = memdesc->size;
626 struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt;
627
628 /* All GPU addresses as assigned are page aligned, but some
629 functions purturb the gpuaddr with an offset, so apply the
630 mask here to make sure we have the right address */
631
632 unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK;
633
634 numpages = (range >> PAGE_SHIFT);
635 if (range & (PAGE_SIZE - 1))
636 numpages++;
637
638 ptefirst = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, gpuaddr);
639 ptelast = ptefirst + numpages;
640
641 superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1));
642 GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE);
643 for (pte = ptefirst; pte < ptelast; pte++) {
644#ifdef VERBOSE_DEBUG
645 /* check if PTE exists */
646 if (!kgsl_pt_map_get(gpummu_pt, pte))
647 KGSL_CORE_ERR("pt entry %x is already "
648 "unmapped for pagetable %p\n", pte, gpummu_pt);
649#endif
650 kgsl_pt_map_set(gpummu_pt, pte, GSL_PT_PAGE_DIRTY);
651 superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1));
652 if (pte == superpte)
653 GSL_TLBFLUSH_FILTER_SETDIRTY(superpte /
654 GSL_PT_SUPER_PTE);
655 }
656
657 /* Post all writes to the pagetable */
658 wmb();
659
660 return 0;
661}
662
Jordan Croused17e9aa2011-10-12 16:57:48 -0600663#define SUPERPTE_IS_DIRTY(_p) \
664(((_p) & (GSL_PT_SUPER_PTE - 1)) == 0 && \
665GSL_TLBFLUSH_FILTER_ISDIRTY((_p) / GSL_PT_SUPER_PTE))
666
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600667static int
668kgsl_gpummu_map(void *mmu_specific_pt,
669 struct kgsl_memdesc *memdesc,
670 unsigned int protflags)
671{
Jordan Croused17e9aa2011-10-12 16:57:48 -0600672 unsigned int pte;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600673 struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt;
Jordan Croused17e9aa2011-10-12 16:57:48 -0600674 struct scatterlist *s;
675 int flushtlb = 0;
676 int i;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600677
Jordan Croused17e9aa2011-10-12 16:57:48 -0600678 pte = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, memdesc->gpuaddr);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600679
Jordan Croused17e9aa2011-10-12 16:57:48 -0600680 /* Flush the TLB if the first PTE isn't at the superpte boundary */
681 if (pte & (GSL_PT_SUPER_PTE - 1))
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600682 flushtlb = 1;
683
Jordan Croused17e9aa2011-10-12 16:57:48 -0600684 for_each_sg(memdesc->sg, s, memdesc->sglen, i) {
685 unsigned int paddr = sg_phys(s);
686 unsigned int j;
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600687
Jordan Croused17e9aa2011-10-12 16:57:48 -0600688 /* Each sg entry might be multiple pages long */
689 for (j = paddr; j < paddr + s->length; pte++, j += PAGE_SIZE) {
690 if (SUPERPTE_IS_DIRTY(pte))
691 flushtlb = 1;
692 kgsl_pt_map_set(gpummu_pt, pte, j | protflags);
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600693 }
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600694 }
695
Jordan Croused17e9aa2011-10-12 16:57:48 -0600696 /* Flush the TLB if the last PTE isn't at the superpte boundary */
697 if ((pte + 1) & (GSL_PT_SUPER_PTE - 1))
698 flushtlb = 1;
699
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600700 wmb();
701
Shubhraprakash Das767fdda2011-08-15 15:49:45 -0600702 if (flushtlb) {
703 /*set all devices as needing flushing*/
704 gpummu_pt->tlb_flags = UINT_MAX;
705 GSL_TLBFLUSH_FILTER_RESET();
706 }
707
708 return 0;
709}
710
711static int kgsl_gpummu_stop(struct kgsl_device *device)
712{
713 struct kgsl_mmu *mmu = &device->mmu;
714
715 kgsl_regwrite(device, MH_MMU_CONFIG, 0x00000000);
716 mmu->flags &= ~KGSL_FLAGS_STARTED;
717
718 return 0;
719}
720
721static int kgsl_gpummu_close(struct kgsl_device *device)
722{
723 /*
724 * close device mmu
725 *
726 * call this with the global lock held
727 */
728 struct kgsl_mmu *mmu = &device->mmu;
729
730 if (mmu->setstate_memory.gpuaddr)
731 kgsl_sharedmem_free(&mmu->setstate_memory);
732
733 if (mmu->defaultpagetable)
734 kgsl_mmu_putpagetable(mmu->defaultpagetable);
735
736 return 0;
737}
738
739static unsigned int
740kgsl_gpummu_get_current_ptbase(struct kgsl_device *device)
741{
742 unsigned int ptbase;
743 kgsl_regread(device, MH_MMU_PT_BASE, &ptbase);
744 return ptbase;
745}
746
747struct kgsl_mmu_ops gpummu_ops = {
748 .mmu_init = kgsl_gpummu_init,
749 .mmu_close = kgsl_gpummu_close,
750 .mmu_start = kgsl_gpummu_start,
751 .mmu_stop = kgsl_gpummu_stop,
752 .mmu_setstate = kgsl_gpummu_setstate,
753 .mmu_device_setstate = kgsl_gpummu_default_setstate,
754 .mmu_pagefault = kgsl_gpummu_pagefault,
755 .mmu_get_current_ptbase = kgsl_gpummu_get_current_ptbase,
756};
757
758struct kgsl_mmu_pt_ops gpummu_pt_ops = {
759 .mmu_map = kgsl_gpummu_map,
760 .mmu_unmap = kgsl_gpummu_unmap,
761 .mmu_create_pagetable = kgsl_gpummu_create_pagetable,
762 .mmu_destroy_pagetable = kgsl_gpummu_destroy_pagetable,
763 .mmu_pt_equal = kgsl_gpummu_pt_equal,
764 .mmu_pt_get_flags = kgsl_gpummu_pt_get_flags,
765};