]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c
ENGR00240988: gpu: copy gpu-viv driver from 3.5.7 kernel
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_mmu_vg.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2013 by Vivante Corp.
4 *
5 *    This program is free software; you can redistribute it and/or modify
6 *    it under the terms of the GNU General Public License as published by
7 *    the Free Software Foundation; either version 2 of the license, or
8 *    (at your option) any later version.
9 *
10 *    This program is distributed in the hope that it will be useful,
11 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 *    GNU General Public License for more details.
14 *
15 *    You should have received a copy of the GNU General Public License
16 *    along with this program; if not write to the Free Software
17 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 *****************************************************************************/
20
21
22 #include "gc_hal_kernel_precomp.h"
23
24 #if gcdENABLE_VG
25
26 #define _GC_OBJ_ZONE    gcvZONE_MMU
27
28 /*******************************************************************************
29 **
30 **  gckVGMMU_Construct
31 **
32 **  Construct a new gckVGMMU object.
33 **
34 **  INPUT:
35 **
36 **      gckVGKERNEL Kernel
37 **          Pointer to an gckVGKERNEL object.
38 **
39 **      gctSIZE_T MmuSize
40 **          Number of bytes for the page table.
41 **
42 **  OUTPUT:
43 **
44 **      gckVGMMU * Mmu
45 **          Pointer to a variable that receives the gckVGMMU object pointer.
46 */
47 gceSTATUS gckVGMMU_Construct(
48     IN gckVGKERNEL Kernel,
49     IN gctSIZE_T MmuSize,
50     OUT gckVGMMU * Mmu
51     )
52 {
53     gckOS os;
54     gckVGHARDWARE hardware;
55     gceSTATUS status;
56     gckVGMMU mmu;
57     gctUINT32 * pageTable;
58     gctUINT32 i;
59
60     gcmkHEADER_ARG("Kernel=0x%x MmuSize=0x%x Mmu=0x%x", Kernel, MmuSize, Mmu);
61
62     /* Verify the arguments. */
63     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
64     gcmkVERIFY_ARGUMENT(MmuSize > 0);
65     gcmkVERIFY_ARGUMENT(Mmu != gcvNULL);
66
67     /* Extract the gckOS object pointer. */
68     os = Kernel->os;
69     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
70
71     /* Extract the gckVGHARDWARE object pointer. */
72     hardware = Kernel->hardware;
73     gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
74
75     /* Allocate memory for the gckVGMMU object. */
76     status = gckOS_Allocate(os, sizeof(struct _gckVGMMU), (gctPOINTER *) &mmu);
77
78     if (status < 0)
79     {
80         /* Error. */
81         gcmkFATAL(
82             "%s(%d): could not allocate gckVGMMU object.",
83             __FUNCTION__, __LINE__
84             );
85
86         gcmkFOOTER();
87         return status;
88     }
89
90     /* Initialize the gckVGMMU object. */
91     mmu->object.type = gcvOBJ_MMU;
92     mmu->os = os;
93     mmu->hardware = hardware;
94
95     /* Create the mutex. */
96     status = gckOS_CreateMutex(os, &mmu->mutex);
97
98     if (status < 0)
99     {
100         /* Roll back. */
101         mmu->object.type = gcvOBJ_UNKNOWN;
102         gcmkVERIFY_OK(gckOS_Free(os, mmu));
103
104         gcmkFOOTER();
105         /* Error. */
106         return status;
107     }
108
109     /* Allocate the page table. */
110     mmu->pageTableSize = MmuSize;
111     status = gckOS_AllocateContiguous(os,
112                                       gcvFALSE,
113                                       &mmu->pageTableSize,
114                                       &mmu->pageTablePhysical,
115                                       &mmu->pageTableLogical);
116
117     if (status < 0)
118     {
119         /* Roll back. */
120         gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex));
121
122         mmu->object.type = gcvOBJ_UNKNOWN;
123         gcmkVERIFY_OK(gckOS_Free(os, mmu));
124
125         /* Error. */
126         gcmkFATAL(
127             "%s(%d): could not allocate page table.",
128             __FUNCTION__, __LINE__
129             );
130
131         gcmkFOOTER();
132         return status;
133     }
134
135     /* Compute number of entries in page table. */
136     mmu->entryCount = mmu->pageTableSize / sizeof(gctUINT32);
137     mmu->entry = 0;
138
139     /* Mark the entire page table as available. */
140     pageTable = (gctUINT32 *) mmu->pageTableLogical;
141     for (i = 0; i < mmu->entryCount; i++)
142     {
143         pageTable[i] = (gctUINT32)~0;
144     }
145
146     /* Set page table address. */
147     status = gckVGHARDWARE_SetMMU(hardware, mmu->pageTableLogical);
148
149     if (status < 0)
150     {
151         /* Free the page table. */
152         gcmkVERIFY_OK(gckOS_FreeContiguous(mmu->os,
153                                       mmu->pageTablePhysical,
154                                       mmu->pageTableLogical,
155                                       mmu->pageTableSize));
156
157         /* Roll back. */
158         gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex));
159
160         mmu->object.type = gcvOBJ_UNKNOWN;
161         gcmkVERIFY_OK(gckOS_Free(os, mmu));
162
163         /* Error. */
164         gcmkFATAL(
165             "%s(%d): could not program page table.",
166             __FUNCTION__, __LINE__
167             );
168
169         gcmkFOOTER();
170         return status;
171     }
172
173     /* Return the gckVGMMU object pointer. */
174     *Mmu = mmu;
175
176     gcmkTRACE_ZONE(
177         gcvLEVEL_INFO, gcvZONE_MMU,
178         "%s(%d): %u entries at %p.(0x%08X)\n",
179         __FUNCTION__, __LINE__,
180         mmu->entryCount,
181         mmu->pageTableLogical,
182         mmu->pageTablePhysical
183         );
184
185     gcmkFOOTER_NO();
186     /* Success. */
187     return gcvSTATUS_OK;
188 }
189
190 /*******************************************************************************
191 **
192 **  gckVGMMU_Destroy
193 **
194 **  Destroy a nAQMMU object.
195 **
196 **  INPUT:
197 **
198 **      gckVGMMU Mmu
199 **          Pointer to an gckVGMMU object.
200 **
201 **  OUTPUT:
202 **
203 **      Nothing.
204 */
205 gceSTATUS gckVGMMU_Destroy(
206     IN gckVGMMU Mmu
207     )
208 {
209     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
210
211     /* Verify the arguments. */
212     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
213
214     /* Free the page table. */
215     gcmkVERIFY_OK(gckOS_FreeContiguous(Mmu->os,
216                                   Mmu->pageTablePhysical,
217                                   Mmu->pageTableLogical,
218                                   Mmu->pageTableSize));
219
220     /* Roll back. */
221     gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->mutex));
222
223     /* Mark the gckVGMMU object as unknown. */
224     Mmu->object.type = gcvOBJ_UNKNOWN;
225
226     /* Free the gckVGMMU object. */
227     gcmkVERIFY_OK(gckOS_Free(Mmu->os, Mmu));
228
229     gcmkFOOTER_NO();
230     /* Success. */
231     return gcvSTATUS_OK;
232 }
233
234 /*******************************************************************************
235 **
236 **  gckVGMMU_AllocatePages
237 **
238 **  Allocate pages inside the page table.
239 **
240 **  INPUT:
241 **
242 **      gckVGMMU Mmu
243 **          Pointer to an gckVGMMU object.
244 **
245 **      gctSIZE_T PageCount
246 **          Number of pages to allocate.
247 **
248 **  OUTPUT:
249 **
250 **      gctPOINTER * PageTable
251 **          Pointer to a variable that receives the base address of the page
252 **          table.
253 **
254 **      gctUINT32 * Address
255 **          Pointer to a variable that receives the hardware specific address.
256 */
257 gceSTATUS gckVGMMU_AllocatePages(
258     IN gckVGMMU Mmu,
259     IN gctSIZE_T PageCount,
260     OUT gctPOINTER * PageTable,
261     OUT gctUINT32 * Address
262     )
263 {
264     gceSTATUS status;
265     gctUINT32 tail, index, i;
266     gctUINT32 * table;
267     gctBOOL allocated = gcvFALSE;
268
269     gcmkHEADER_ARG("Mmu=0x%x PageCount=0x%x PageTable=0x%x Address=0x%x",
270         Mmu, PageCount, PageTable, Address);
271
272     /* Verify the arguments. */
273     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
274     gcmkVERIFY_ARGUMENT(PageCount > 0);
275     gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
276     gcmkVERIFY_ARGUMENT(Address != gcvNULL);
277
278     gcmkTRACE_ZONE(
279         gcvLEVEL_INFO, gcvZONE_MMU,
280         "%s(%d): %u pages.\n",
281         __FUNCTION__, __LINE__,
282         PageCount
283         );
284
285     if (PageCount > Mmu->entryCount)
286     {
287         gcmkTRACE_ZONE(
288             gcvLEVEL_ERROR, gcvZONE_MMU,
289             "%s(%d): page table too small for %u pages.\n",
290             __FUNCTION__, __LINE__,
291             PageCount
292             );
293
294         gcmkFOOTER_NO();
295         /* Not enough pages avaiable. */
296         return gcvSTATUS_OUT_OF_RESOURCES;
297     }
298
299     /* Grab the mutex. */
300     status = gckOS_AcquireMutex(Mmu->os, Mmu->mutex, gcvINFINITE);
301
302     if (status < 0)
303     {
304         gcmkTRACE_ZONE(
305             gcvLEVEL_ERROR, gcvZONE_MMU,
306             "%s(%d): could not acquire mutex.\n"
307             ,__FUNCTION__, __LINE__
308             );
309
310         gcmkFOOTER();
311         /* Error. */
312         return status;
313     }
314
315     /* Compute the tail for this allocation. */
316     tail = Mmu->entryCount - PageCount;
317
318     /* Walk all entries until we find enough slots. */
319     for (index = Mmu->entry; index <= tail;)
320     {
321         /* Access page table. */
322         table = (gctUINT32 *) Mmu->pageTableLogical + index;
323
324         /* See if all slots are available. */
325         for (i = 0; i < PageCount; i++, table++)
326         {
327             if (*table != ~0)
328             {
329                 /* Start from next slot. */
330                 index += i + 1;
331                 break;
332             }
333         }
334
335         if (i == PageCount)
336         {
337             /* Bail out if we have enough page entries. */
338             allocated = gcvTRUE;
339             break;
340         }
341     }
342
343     if (!allocated)
344     {
345         if (status >= 0)
346         {
347             /* Walk all entries until we find enough slots. */
348             for (index = 0; index <= tail;)
349             {
350                 /* Access page table. */
351                 table = (gctUINT32 *) Mmu->pageTableLogical + index;
352
353                 /* See if all slots are available. */
354                 for (i = 0; i < PageCount; i++, table++)
355                 {
356                     if (*table != ~0)
357                     {
358                         /* Start from next slot. */
359                         index += i + 1;
360                         break;
361                     }
362                 }
363
364                 if (i == PageCount)
365                 {
366                     /* Bail out if we have enough page entries. */
367                     allocated = gcvTRUE;
368                     break;
369                 }
370             }
371         }
372     }
373
374     if (!allocated && (status >= 0))
375     {
376         gcmkTRACE_ZONE(
377             gcvLEVEL_ERROR, gcvZONE_MMU,
378             "%s(%d): not enough free pages for %u pages.\n",
379             __FUNCTION__, __LINE__,
380             PageCount
381             );
382
383         /* Not enough empty slots available. */
384         status = gcvSTATUS_OUT_OF_RESOURCES;
385     }
386
387     if (status >= 0)
388     {
389         /* Build virtual address. */
390         status = gckVGHARDWARE_BuildVirtualAddress(Mmu->hardware,
391                                                  index,
392                                                  0,
393                                                  Address);
394
395         if (status >= 0)
396         {
397             /* Update current entry into page table. */
398             Mmu->entry = index + PageCount;
399
400             /* Return pointer to page table. */
401             *PageTable = (gctUINT32 *)  Mmu->pageTableLogical + index;
402
403             gcmkTRACE_ZONE(
404                 gcvLEVEL_INFO, gcvZONE_MMU,
405                 "%s(%d): allocated %u pages at index %u (0x%08X) @ %p.\n",
406                 __FUNCTION__, __LINE__,
407                 PageCount,
408                 index,
409                 *Address,
410                 *PageTable
411                 );
412             }
413     }
414
415     /* Release the mutex. */
416     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->mutex));
417     gcmkFOOTER();
418
419     /* Return status. */
420     return status;
421 }
422
423 /*******************************************************************************
424 **
425 **  gckVGMMU_FreePages
426 **
427 **  Free pages inside the page table.
428 **
429 **  INPUT:
430 **
431 **      gckVGMMU Mmu
432 **          Pointer to an gckVGMMU object.
433 **
434 **      gctPOINTER PageTable
435 **          Base address of the page table to free.
436 **
437 **      gctSIZE_T PageCount
438 **          Number of pages to free.
439 **
440 **  OUTPUT:
441 **
442 **      Nothing.
443 */
444 gceSTATUS gckVGMMU_FreePages(
445     IN gckVGMMU Mmu,
446     IN gctPOINTER PageTable,
447     IN gctSIZE_T PageCount
448     )
449 {
450     gctUINT32 * table;
451
452     gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=0x%x",
453         Mmu, PageTable, PageCount);
454
455     /* Verify the arguments. */
456     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
457     gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
458     gcmkVERIFY_ARGUMENT(PageCount > 0);
459
460     gcmkTRACE_ZONE(
461         gcvLEVEL_INFO, gcvZONE_MMU,
462         "%s(%d): freeing %u pages at index %u @ %p.\n",
463         __FUNCTION__, __LINE__,
464         PageCount,
465         ((gctUINT32 *) PageTable - (gctUINT32 *) Mmu->pageTableLogical),
466         PageTable
467         );
468
469     /* Convert pointer. */
470     table = (gctUINT32 *) PageTable;
471
472     /* Mark the page table entries as available. */
473     while (PageCount-- > 0)
474     {
475         *table++ = (gctUINT32)~0;
476     }
477
478     gcmkFOOTER_NO();
479     /* Success. */
480     return gcvSTATUS_OK;
481 }
482
483 gceSTATUS
484 gckVGMMU_SetPage(
485     IN gckVGMMU Mmu,
486     IN gctUINT32 PageAddress,
487     IN gctUINT32 *PageEntry
488     )
489 {
490     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
491
492     /* Verify the arguments. */
493     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
494     gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL);
495     gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
496
497     *PageEntry = PageAddress;
498
499     /* Success. */
500     gcmkFOOTER_NO();
501     return gcvSTATUS_OK;
502 }
503
504 gceSTATUS
505 gckVGMMU_Flush(
506    IN gckVGMMU Mmu
507    )
508 {
509     gckVGHARDWARE hardware;
510
511     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
512
513     hardware = Mmu->hardware;
514     gcmkVERIFY_OK(
515         gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
516
517     /* Success. */
518     gcmkFOOTER_NO();
519     return gcvSTATUS_OK;
520 }
521
522 #endif /* gcdENABLE_VG */