blob: 726e555fe6c46d95cd42f96af5b51c2717845749 [file] [log] [blame]
/****************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2014 - 2020 Vivante Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*****************************************************************************
*
* The GPL License (GPL)
*
* Copyright (C) 2014 - 2020 Vivante Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*****************************************************************************
*
* Note: This software is released under dual MIT and GPL licenses. A
* recipient may use this file under the terms of either the MIT license or
* GPL License. If you wish to use only one license not the other, you can
* indicate your decision by deleting one of the above license notices in your
* version of this file.
*
*****************************************************************************/
#include "gc_hal_kernel_linux.h"
#include "gc_hal_kernel_allocator.h"
#include <linux/pagemap.h>
#include <linux/seq_file.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/sched.h>
#define _GC_OBJ_ZONE gcvZONE_DEVICE
#define DEBUG_FILE "galcore_trace"
#define PARENT_FILE "gpu"
#define gcdDEBUG_FS_WARN "Experimental debug entry, may be removed in future release, do NOT rely on it!\n"
static gckGALDEVICE galDevice;
extern gcTA globalTA[16];
/******************************************************************************\
******************************** Debugfs Support *******************************
\******************************************************************************/
/******************************************************************************\
***************************** DEBUG SHOW FUNCTIONS *****************************
\******************************************************************************/
int gc_info_show(struct seq_file* m, void* data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
int i = 0;
gceCHIPMODEL chipModel = 0;
gctUINT32 chipRevision = 0;
gctUINT32 productID = 0;
gctUINT32 ecoID = 0;
if (!device)
return -ENXIO;
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (device->kernels[i])
{
if (i == gcvCORE_VG)
{
}
else
{
chipModel = device->kernels[i]->hardware->identity.chipModel;
chipRevision = device->kernels[i]->hardware->identity.chipRevision;
productID = device->kernels[i]->hardware->identity.productID;
ecoID = device->kernels[i]->hardware->identity.ecoID;
}
seq_printf(m, "gpu : %d\n", i);
seq_printf(m, "model : %4x\n", chipModel);
seq_printf(m, "revision : %4x\n", chipRevision);
seq_printf(m, "product : %4x\n", productID);
seq_printf(m, "eco : %4x\n", ecoID);
seq_printf(m, "\n");
}
}
return 0;
}
int gc_clients_show(struct seq_file* m, void* data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = _GetValidKernel(device);
gcsDATABASE_PTR database;
gctINT i, pid;
char name[24];
if (!kernel)
return -ENXIO;
seq_printf(m, "%-8s%s\n", "PID", "NAME");
seq_printf(m, "------------------------\n");
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
/* Walk the databases. */
for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
pid = database->processID;
gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name));
seq_printf(m, "%-8d%s\n", pid, name);
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
/* Success. */
return 0;
}
int gc_meminfo_show(struct seq_file* m, void* data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = _GetValidKernel(device);
gckVIDMEM memory;
gceSTATUS status;
gcsDATABASE_PTR database;
gctUINT32 i;
gctUINT32 free = 0, used = 0, total = 0, minFree = 0, maxUsed = 0;
gcsDATABASE_COUNTERS virtualCounter = {0, 0, 0};
gcsDATABASE_COUNTERS nonPagedCounter = {0, 0, 0};
if (!kernel)
return -ENXIO;
status = gckKERNEL_GetVideoMemoryPool(kernel, gcvPOOL_SYSTEM, &memory);
if (gcmIS_SUCCESS(status))
{
gcmkVERIFY_OK(
gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));
free = memory->freeBytes;
minFree = memory->minFreeBytes;
used = memory->bytes - memory->freeBytes;
maxUsed = memory->bytes - memory->minFreeBytes;
total = memory->bytes;
gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
}
seq_printf(m, "VIDEO MEMORY:\n");
seq_printf(m, " POOL SYSTEM:\n");
seq_printf(m, " Free : %10u B\n", free);
seq_printf(m, " Used : %10u B\n", used);
seq_printf(m, " MinFree : %10u B\n", minFree);
seq_printf(m, " MaxUsed : %10u B\n", maxUsed);
seq_printf(m, " Total : %10u B\n", total);
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
/* Walk the databases. */
for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
gcsDATABASE_COUNTERS * counter;
counter = &database->vidMemPool[gcvPOOL_VIRTUAL];
virtualCounter.bytes += counter->bytes;
virtualCounter.maxBytes += counter->maxBytes;
counter = &database->nonPaged;
nonPagedCounter.bytes += counter->bytes;
nonPagedCounter.maxBytes += counter->maxBytes;
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
seq_printf(m, " POOL VIRTUAL:\n");
seq_printf(m, " Used : %10llu B\n", virtualCounter.bytes);
seq_printf(m, " MaxUsed : %10llu B\n", virtualCounter.maxBytes);
return 0;
}
static const char * vidmemTypeStr[gcvVIDMEM_TYPE_COUNT] =
{
"Generic",
"Index",
"Vertex",
"Texture",
"RenderTarget",
"Depth",
"Bitmap",
"TileStatus",
"Image",
"Mask",
"Scissor",
"HZ",
"ICache",
"TxDesc",
"Fence",
"TFBHeader",
"Command",
};
static const char * poolStr[gcvPOOL_NUMBER_OF_POOLS] =
{
"Unknown",
"Default",
"Local",
"Internal",
"External",
"Unified",
"System",
"Sram",
"Virtual",
"User",
"Insram",
"Exsram",
};
static void
_ShowDummyRecord(
IN struct seq_file *File,
IN gcsDATABASE_PTR Database
)
{
}
static void
_ShowVideoMemoryRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
gctUINT32 handle;
gckVIDMEM_NODE nodeObject;
gctPHYS_ADDR_T physical;
gctINT32 refCount = 0;
gctINT32 lockCount = 0;
gceSTATUS status;
seq_printf(m, "Video Memory Node:\n");
seq_printf(m, " handle nodeObject size type pool physical ref lock\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_VIDEO_MEMORY)
{
continue;
}
handle = gcmPTR2INT32(record->data);
status = gckVIDMEM_HANDLE_Lookup2(
record->kernel,
Database,
handle,
&nodeObject
);
if (gcmIS_ERROR(status))
{
seq_printf(m, "%6u Invalid Node\n", handle);
continue;
}
gcmkONERROR(gckVIDMEM_NODE_GetPhysical(record->kernel, nodeObject, 0, &physical));
gcmkONERROR(gckVIDMEM_NODE_GetReference(record->kernel, nodeObject, &refCount));
gcmkONERROR(gckVIDMEM_NODE_GetLockCount(record->kernel, nodeObject, &lockCount));
seq_printf(m, "%#8x %#18lx %10lu %12s %8s %#12llx %4d %4d\n",
handle,
(unsigned long)nodeObject,
(unsigned long)record->bytes,
vidmemTypeStr[nodeObject->type],
poolStr[nodeObject->pool],
physical,
refCount,
lockCount
);
}
}
OnError:
return;
}
static void
_ShowCommandBufferRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
return;
}
static void
_ShowNonPagedRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
seq_printf(m, "NonPaged Memory:\n");
seq_printf(m, " name vaddr size\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_NON_PAGED)
{
continue;
}
seq_printf(m, "%6u %#18lx %10lu\n",
gcmPTR2INT32(record->physical),
(unsigned long)record->data,
(unsigned long)record->bytes
);
}
}
}
static void
_ShowContiguousRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
return;
}
static void
_ShowSignalRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
seq_printf(m, "User signal:\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_SIGNAL)
{
continue;
}
seq_printf(m, "%#10x\n", gcmPTR2INT32(record->data));
}
}
}
static void
_ShowLockRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
gceSTATUS status;
gctUINT32 handle;
gckVIDMEM_NODE nodeObject;
seq_printf(m, "Video Memory Lock:\n");
seq_printf(m, " handle nodeObject vaddr\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_VIDEO_MEMORY_LOCKED)
{
continue;
}
handle = gcmPTR2INT32(record->data);
status = gckVIDMEM_HANDLE_Lookup2(
record->kernel,
Database,
handle,
&nodeObject
);
if (gcmIS_ERROR(status))
{
nodeObject = gcvNULL;
}
seq_printf(m, "%#8x %#18lx %#18lx\n",
handle,
(unsigned long)nodeObject,
(unsigned long)record->physical
);
}
}
}
static void
_ShowContextRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
seq_printf(m, "Context:\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_CONTEXT)
{
continue;
}
seq_printf(m, "%6u\n", gcmPTR2INT32(record->data));
}
}
}
static void
_ShowMapMemoryRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
seq_printf(m, "Map Memory:\n");
seq_printf(m, " name vaddr size\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_MAP_MEMORY)
{
continue;
}
seq_printf(m, "%#6lx %#18lx %10lu\n",
(unsigned long)record->physical,
(unsigned long)record->data,
(unsigned long)record->bytes
);
}
}
}
static void
_ShowMapUserMemoryRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
return;
}
static void
_ShowShbufRecord(
IN struct seq_file *m,
IN gcsDATABASE_PTR Database
)
{
gctUINT i;
seq_printf(m, "ShBuf:\n");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR r = Database->list[i];
while (r != NULL)
{
gcsDATABASE_RECORD_PTR record = r;
r = r->next;
if (record->type != gcvDB_SHBUF)
{
continue;
}
seq_printf(m, "%#8x\n", gcmPTR2INT32(record->data));
}
}
}
static void
_ShowCounters(
struct seq_file *File,
gcsDATABASE_PTR Database
)
{
gctUINT i = 0;
static const char * otherCounterNames[] = {
"AllocNonPaged",
"AllocContiguous",
"MapUserMemory",
"MapMemory",
};
gcsDATABASE_COUNTERS * otherCounters[] = {
&Database->nonPaged,
&Database->contiguous,
&Database->mapUserMemory,
&Database->mapMemory,
};
seq_printf(File, "%-16s %16s %16s %16s\n", "", "Current", "Maximum", "Total");
/* Print surface type counters. */
seq_printf(File, "%-16s %16lld %16lld %16lld\n",
"All-Types",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvVIDMEM_TYPE_COUNT; i++)
{
seq_printf(File, "%-16s %16lld %16lld %16lld\n",
vidmemTypeStr[i],
Database->vidMemType[i].bytes,
Database->vidMemType[i].maxBytes,
Database->vidMemType[i].totalBytes);
}
seq_puts(File, "\n");
/* Print surface pool counters. */
seq_printf(File, "%-16s %16lld %16lld %16lld\n",
"All-Pools",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++)
{
seq_printf(File, "%-16s %16lld %16lld %16lld\n",
poolStr[i],
Database->vidMemPool[i].bytes,
Database->vidMemPool[i].maxBytes,
Database->vidMemPool[i].totalBytes);
}
seq_puts(File, "\n");
/* Print other counters. */
for (i = 0; i < gcmCOUNTOF(otherCounterNames); i++)
{
seq_printf(File, "%-16s %16lld %16lld %16lld\n",
otherCounterNames[i],
otherCounters[i]->bytes,
otherCounters[i]->maxBytes,
otherCounters[i]->totalBytes);
}
seq_puts(File, "\n");
}
static int
_ShowRecord(
IN struct seq_file *File,
IN gcsDATABASE_PTR Database,
IN gcsDATABASE_RECORD_PTR Record
)
{
gctUINT32 handle;
gckVIDMEM_NODE nodeObject;
gctPHYS_ADDR_T physical;
gceSTATUS status = gcvSTATUS_OK;
static const char * recordTypes[gcvDB_NUM_TYPES] = {
"Unknown",
"VideoMemory",
"CommandBuffer",
"NonPaged",
"Contiguous",
"Signal",
"VidMemLock",
"Context",
"Idel",
"MapMemory",
"MapUserMemory",
"ShBuf",
};
handle = gcmPTR2INT32(Record->data);
if (Record->type == gcvDB_VIDEO_MEMORY || Record->type == gcvDB_VIDEO_MEMORY_LOCKED)
{
status = gckVIDMEM_HANDLE_Lookup2(
Record->kernel,
Database,
handle,
&nodeObject
);
if (gcmIS_ERROR(status))
{
seq_printf(File, "%6u Invalid Node\n", handle);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
gcmkONERROR(gckVIDMEM_NODE_GetPhysical(Record->kernel, nodeObject, 0, &physical));
}
else
{
physical = (gctUINT64)(gctUINTPTR_T)Record->physical;
}
seq_printf(File, "%-14s %3d %16x %16zx %16zu\n",
recordTypes[Record->type],
Record->kernel->core,
gcmPTR2INT32(Record->data),
(size_t) physical,
Record->bytes
);
OnError:
return status;
}
static void
_ShowDataBaseOldFormat(
IN struct seq_file *File,
IN gcsDATABASE_PTR Database
)
{
gctINT pid;
gctUINT i;
char name[24];
/* Process ID and name */
pid = Database->processID;
gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name));
seq_printf(File, "--------------------------------------------------------------------------------\n");
seq_printf(File, "Process: %-8d %s\n", pid, name);
seq_printf(File, "Records:\n");
seq_printf(File, "%14s %3s %16s %16s %16s\n",
"Type", "GPU", "Data/Node", "Physical/Node", "Bytes");
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR record = Database->list[i];
while (record != NULL)
{
_ShowRecord(File, Database, record);
record = record->next;
}
}
seq_printf(File, "Counters:\n");
_ShowCounters(File, Database);
}
static void
_ShowDatabase(
IN struct seq_file *File,
IN gcsDATABASE_PTR Database
)
{
gctINT pid;
gctUINT i;
char name[24];
gctBOOL hasType[gcvDB_NUM_TYPES] = {0,};
void (* showFuncs[])(struct seq_file *, gcsDATABASE_PTR) =
{
_ShowDummyRecord,
_ShowVideoMemoryRecord,
_ShowCommandBufferRecord,
_ShowNonPagedRecord,
_ShowContiguousRecord,
_ShowSignalRecord,
_ShowLockRecord,
_ShowContextRecord,
_ShowDummyRecord,
_ShowMapMemoryRecord,
_ShowMapUserMemoryRecord,
_ShowShbufRecord,
};
gcmSTATIC_ASSERT(gcmCOUNTOF(showFuncs) == gcvDB_NUM_TYPES,
"DB type mismatch");
/* Process ID and name */
pid = Database->processID;
gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name));
seq_printf(File, "--------------------------------------------------------------------------------\n");
seq_printf(File, "Process: %-8d %s\n", pid, name);
for (i = 0; i < gcmCOUNTOF(Database->list); i++)
{
gcsDATABASE_RECORD_PTR record = Database->list[i];
while (record != NULL)
{
hasType[record->type] = gcvTRUE;
record = record->next;
}
}
for (i = 0; i < gcvDB_NUM_TYPES; i++)
{
if (hasType[i])
{
showFuncs[i](File, Database);
}
}
}
static int
gc_db_show_old(struct seq_file *m, void *data)
{
gcsDATABASE_PTR database;
gctINT i;
static gctUINT64 idleTime = 0;
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = _GetValidKernel(device);
if (!kernel)
return -ENXIO;
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
if (kernel->db->idleTime)
{
/* Record idle time if DB upated. */
idleTime = kernel->db->idleTime;
kernel->db->idleTime = 0;
}
/* Idle time since last call */
seq_printf(m, "GPU Idle: %llu ns\n", idleTime);
/* Walk the databases. */
for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
_ShowDataBaseOldFormat(m, database);
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
return 0 ;
}
static int
gc_db_show(struct seq_file *m, void *data)
{
gcsDATABASE_PTR database;
gctINT i;
static gctUINT64 idleTime = 0;
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = _GetValidKernel(device);
if (!kernel)
return -ENXIO;
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
if (kernel->db->idleTime)
{
/* Record idle time if DB upated. */
idleTime = kernel->db->idleTime;
kernel->db->idleTime = 0;
}
/* Idle time since last call */
seq_printf(m, "GPU Idle: %llu ns\n", idleTime);
/* Walk the databases. */
for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
_ShowDatabase(m, database);
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
return 0 ;
}
static int
gc_version_show(struct seq_file *m, void *data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gcsPLATFORM * platform = gcvNULL;
if (!device)
return -ENXIO;
platform = device->platform;
if (!platform)
return -ENXIO;
seq_printf(m, "%s built at %s\n", gcvVERSION_STRING, HOST);
if (platform->name)
{
seq_printf(m, "Platform path: %s\n", platform->name);
}
else
{
seq_printf(m, "Code path: %s\n", __FILE__);
}
return 0 ;
}
static void print_ull(char dest[32], unsigned long long u)
{
unsigned t[7];
int i;
if (u < 1000)
{
sprintf(dest, "%27llu", u);
return;
}
for (i = 0; i < 7 && u; i++)
{
t[i] = do_div(u, 1000);
}
dest += sprintf(dest, "%*s", (7 - i) * 4, "");
dest += sprintf(dest, "%3u", t[--i]);
for (i--; i >= 0; i--)
{
dest += sprintf(dest, ",%03u", t[i]);
}
}
/*******************************************************************************
**
** Show PM state timer.
**
** Entry is called as 'idle' for compatible reason, it shows more information
** than idle actually.
**
** Start: Start time of this counting period.
** End: End time of this counting peroid.
** On: Time GPU stays in gcvPOWER_0N.
** Off: Time GPU stays in gcvPOWER_0FF.
** Idle: Time GPU stays in gcvPOWER_IDLE.
** Suspend: Time GPU stays in gcvPOWER_SUSPEND.
*/
static int
gc_idle_show(struct seq_file *m, void *data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = _GetValidKernel(device);
char str[32];
gctUINT64 on;
gctUINT64 off;
gctUINT64 idle;
gctUINT64 suspend;
if (!kernel)
return -ENXIO;
gckHARDWARE_QueryStateTimer(kernel->hardware, &on, &off, &idle, &suspend);
/* Idle time since last call */
print_ull(str, on);
seq_printf(m, "On: %s ns\n", str);
print_ull(str, off);
seq_printf(m, "Off: %s ns\n", str);
print_ull(str, idle);
seq_printf(m, "Idle: %s ns\n", str);
print_ull(str, suspend);
seq_printf(m, "Suspend: %s ns\n", str);
return 0 ;
}
extern void
_DumpState(
IN gckKERNEL Kernel
);
/*******************************************************************************
**
** Show PM state timer.
**
** Entry is called as 'idle' for compatible reason, it shows more information
** than idle actually.
**
** Start: Start time of this counting period.
** End: End time of this counting peroid.
** On: Time GPU stays in gcvPOWER_0N.
** Off: Time GPU stays in gcvPOWER_0FF.
** Idle: Time GPU stays in gcvPOWER_IDLE.
** Suspend: Time GPU stays in gcvPOWER_SUSPEND.
*/
static int dumpCore = 0;
static int
gc_dump_trigger_show(struct seq_file *m, void *data)
{
#if gcdENABLE_3D
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gckKERNEL kernel = gcvNULL;
if (dumpCore >= gcvCORE_MAJOR && dumpCore < gcvCORE_COUNT)
{
kernel = device->kernels[dumpCore];
}
if (!kernel)
return -ENXIO;
#endif
seq_printf(m, gcdDEBUG_FS_WARN);
#if gcdENABLE_3D
seq_printf(m, "Get dump from /proc/kmsg or /sys/kernel/debug/gc/galcore_trace\n");
if (kernel && kernel->hardware->options.powerManagement == gcvFALSE)
{
_DumpState(kernel);
}
#endif
return 0;
}
static int dumpProcess = 0;
static void
_ShowVideoMemoryOldFormat(
struct seq_file *File,
gcsDATABASE_PTR Database
)
{
gctUINT i = 0;
static const char * otherCounterNames[] = {
"AllocNonPaged",
"AllocContiguous",
"MapUserMemory",
"MapMemory",
};
gcsDATABASE_COUNTERS * otherCounters[] = {
&Database->nonPaged,
&Database->contiguous,
&Database->mapUserMemory,
&Database->mapMemory,
};
seq_printf(File, "%-16s %16s %16s %16s\n", "", "Current", "Maximum", "Total");
/* Print surface type counters. */
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
"All-Types",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvVIDMEM_TYPE_COUNT; i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
vidmemTypeStr[i],
Database->vidMemType[i].bytes,
Database->vidMemType[i].maxBytes,
Database->vidMemType[i].totalBytes);
}
seq_puts(File, "\n");
/* Print surface pool counters. */
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
"All-Pools",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
poolStr[i],
Database->vidMemPool[i].bytes,
Database->vidMemPool[i].maxBytes,
Database->vidMemPool[i].totalBytes);
}
seq_puts(File, "\n");
/* Print other counters. */
for (i = 0; i < gcmCOUNTOF(otherCounterNames); i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
otherCounterNames[i],
otherCounters[i]->bytes,
otherCounters[i]->maxBytes,
otherCounters[i]->totalBytes);
}
seq_puts(File, "\n");
}
static void
_ShowVideoMemory(
struct seq_file *File,
gcsDATABASE_PTR Database
)
{
gctUINT i = 0;
static const char * otherCounterNames[] = {
"AllocNonPaged",
"MapMemory",
};
gcsDATABASE_COUNTERS * otherCounters[] = {
&Database->nonPaged,
&Database->mapMemory,
};
seq_printf(File, "%-16s %16s %16s %16s\n", "", "Current", "Maximum", "Total");
/* Print surface type counters. */
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
"All-Types",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvVIDMEM_TYPE_COUNT; i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
vidmemTypeStr[i],
Database->vidMemType[i].bytes,
Database->vidMemType[i].maxBytes,
Database->vidMemType[i].totalBytes);
}
seq_puts(File, "\n");
/* Print surface pool counters. */
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
"All-Pools",
Database->vidMem.bytes,
Database->vidMem.maxBytes,
Database->vidMem.totalBytes);
for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
poolStr[i],
Database->vidMemPool[i].bytes,
Database->vidMemPool[i].maxBytes,
Database->vidMemPool[i].totalBytes);
}
seq_puts(File, "\n");
/* Print other counters. */
for (i = 0; i < gcmCOUNTOF(otherCounterNames); i++)
{
seq_printf(File, "%-16s %16llu %16llu %16llu\n",
otherCounterNames[i],
otherCounters[i]->bytes,
otherCounters[i]->maxBytes,
otherCounters[i]->totalBytes);
}
seq_puts(File, "\n");
}
static int gc_vidmem_show_old(struct seq_file *m, void *unused)
{
gceSTATUS status;
gcsDATABASE_PTR database;
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
char name[64];
int i;
gckKERNEL kernel = _GetValidKernel(device);
if (!kernel)
return -ENXIO;
if (dumpProcess == 0)
{
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
for (i = 0; i < gcmCOUNTOF(kernel->db->db); i++)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
gckOS_GetProcessNameByPid(database->processID, gcmSIZEOF(name), name);
seq_printf(m, "VidMem Usage (Process %u: %s):\n", database->processID, name);
_ShowVideoMemoryOldFormat(m, database);
seq_puts(m, "\n");
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
}
else
{
/* Find the database. */
status = gckKERNEL_FindDatabase(kernel, dumpProcess, gcvFALSE, &database);
if (gcmIS_ERROR(status))
{
seq_printf(m, "ERROR: process %d not found\n", dumpProcess);
return 0;
}
gckOS_GetProcessNameByPid(dumpProcess, gcmSIZEOF(name), name);
seq_printf(m, "VidMem Usage (Process %d: %s):\n", dumpProcess, name);
_ShowVideoMemoryOldFormat(m, database);
}
return 0;
}
static int gc_vidmem_show(struct seq_file *m, void *unused)
{
gceSTATUS status;
gcsDATABASE_PTR database;
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
char name[64];
int i;
gckKERNEL kernel = _GetValidKernel(device);
if (!kernel)
return -ENXIO;
if (dumpProcess == 0)
{
/* Acquire the database mutex. */
gcmkVERIFY_OK(
gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
for (i = 0; i < gcmCOUNTOF(kernel->db->db); i++)
{
for (database = kernel->db->db[i];
database != gcvNULL;
database = database->next)
{
gckOS_GetProcessNameByPid(database->processID, gcmSIZEOF(name), name);
seq_printf(m, "VidMem Usage (Process %u: %s):\n", database->processID, name);
_ShowVideoMemory(m, database);
seq_puts(m, "\n");
}
}
/* Release the database mutex. */
gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
}
else
{
/* Find the database. */
status = gckKERNEL_FindDatabase(kernel, dumpProcess, gcvFALSE, &database);
if (gcmIS_ERROR(status))
{
seq_printf(m, "ERROR: process %d not found\n", dumpProcess);
return 0;
}
gckOS_GetProcessNameByPid(dumpProcess, gcmSIZEOF(name), name);
seq_printf(m, "VidMem Usage (Process %d: %s):\n", dumpProcess, name);
_ShowVideoMemory(m, database);
}
return 0;
}
static inline int strtoint_from_user(const char __user *s,
size_t count, int *res)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
int ret = kstrtoint_from_user(s, count, 10, res);
return ret < 0 ? ret : count;
#else
/* sign, base 2 representation, newline, terminator */
char buf[1 + sizeof(long) * 8 + 1 + 1];
size_t len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, s, len))
return -EFAULT;
buf[len] = '\0';
*res = (int) simple_strtol(buf, NULL, 0);
return count;
#endif
}
static int gc_vidmem_write(const char __user *buf, size_t count, void* data)
{
return strtoint_from_user(buf, count, &dumpProcess);
}
static int gc_dump_trigger_write(const char __user *buf, size_t count, void* data)
{
return strtoint_from_user(buf, count, &dumpCore);
}
static int gc_clk_show(struct seq_file* m, void* data)
{
gcsINFO_NODE *node = m->private;
gckGALDEVICE device = node->device;
gctUINT i;
gceSTATUS status;
if (!device)
return -ENXIO;
for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++)
{
if (device->kernels[i])
{
gckHARDWARE hardware = device->kernels[i]->hardware;
if (i == gcvCORE_VG)
{
continue;
}
status = gckHARDWARE_QueryFrequency(hardware);
if (gcmIS_ERROR(status))
{
seq_printf(m, "query gpu%d clock fail.\n", i);
continue;
}
if (hardware->mcClk)
{
seq_printf(m, "gpu%d mc clock: %d HZ.\n", i, hardware->mcClk);
}
if (hardware->shClk)
{
seq_printf(m, "gpu%d sh clock: %d HZ.\n", i, hardware->shClk);
}
}
}
return 0;
}
static gcsINFO InfoList[] =
{
{"info", gc_info_show},
{"clients", gc_clients_show},
{"meminfo", gc_meminfo_show},
{"idle", gc_idle_show},
{"database", gc_db_show_old},
{"database64x", gc_db_show},
{"version", gc_version_show},
{"vidmem", gc_vidmem_show_old, gc_vidmem_write},
{"vidmem64x", gc_vidmem_show, gc_vidmem_write},
{"dump_trigger", gc_dump_trigger_show, gc_dump_trigger_write},
{"clk", gc_clk_show},
};
static gceSTATUS
_DebugfsInit(
IN gckGALDEVICE Device
)
{
gceSTATUS status = gcvSTATUS_OK;
gckDEBUGFS_DIR dir = &Device->debugfsDir;
gcmkONERROR(gckDEBUGFS_DIR_Init(dir, gcvNULL, "gc"));
gcmkONERROR(gckDEBUGFS_DIR_CreateFiles(dir, InfoList, gcmCOUNTOF(InfoList), Device));
OnError:
return status;
}
static void
_DebugfsCleanup(
IN gckGALDEVICE Device
)
{
gckDEBUGFS_DIR dir = &Device->debugfsDir;
if (Device->debugfsDir.root)
{
gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(dir, InfoList, gcmCOUNTOF(InfoList)));
gckDEBUGFS_DIR_Deinit(dir);
}
}
/******************************************************************************\
*************************** Memory Allocation Wrappers *************************
\******************************************************************************/
static gceSTATUS
_AllocateMemory(
IN gckGALDEVICE Device,
IN gctSIZE_T Bytes,
OUT gctPOINTER *Logical,
OUT gctPHYS_ADDR *Physical,
OUT gctUINT64 *PhysAddr
)
{
gceSTATUS status = gcvSTATUS_OK;
gctPHYS_ADDR_T physAddr;
gcmkHEADER_ARG("Device=%p Bytes=0x%zx", Device, Bytes);
gcmkVERIFY_ARGUMENT(Device != NULL);
gcmkVERIFY_ARGUMENT(Logical != NULL);
gcmkVERIFY_ARGUMENT(Physical != NULL);
gcmkVERIFY_ARGUMENT(PhysAddr != NULL);
gcmkONERROR(gckOS_AllocateNonPagedMemory(
Device->os, gcvFALSE, gcvALLOC_FLAG_CONTIGUOUS, &Bytes, Physical, Logical
));
gcmkONERROR(gckOS_GetPhysicalFromHandle(
Device->os, *Physical, 0, &physAddr
));
*PhysAddr = physAddr;
OnError:
gcmkFOOTER_ARG(
"*Logical=%p *Physical=%p *PhysAddr=0x%llx",
gcmOPT_POINTER(Logical), gcmOPT_POINTER(Physical), gcmOPT_VALUE(PhysAddr)
);
return status;
}
static gceSTATUS
_FreeMemory(
IN gckGALDEVICE Device,
IN gctPOINTER Logical,
IN gctPHYS_ADDR Physical
)
{
gceSTATUS status;
gcmkHEADER_ARG("Device=%p Logical=%p Physical=%p",
Device, Logical, Physical);
gcmkVERIFY_ARGUMENT(Device != NULL);
status = gckOS_FreeNonPagedMemory(
Device->os, Physical, Logical,
((PLINUX_MDL) Physical)->numPages * PAGE_SIZE
);
gcmkFOOTER();
return status;
}
static gceSTATUS
_SetupContiguousVidMem(
IN gckGALDEVICE Device,
IN const gcsMODULE_PARAMETERS * Args
)
{
gceSTATUS status = gcvSTATUS_OK;
gctUINT64 physAddr = ~0ULL;
gckGALDEVICE device = Device;
gcmkHEADER_ARG("Device=%p Args=%p", Device, Args);
/* set up the contiguous memory */
device->contiguousBase = Args->contiguousBase;
device->contiguousSize = Args->contiguousSize;
if (Args->contiguousSize == 0)
{
gcmkFOOTER_NO();
return gcvSTATUS_OK;
}
if (Args->contiguousBase == 0)
{
while (device->contiguousSize > 0)
{
/* Allocate contiguous memory. */
status = _AllocateMemory(
device,
device->contiguousSize,
&device->contiguousLogical,
&device->contiguousPhysical,
&physAddr
);
if (gcmIS_SUCCESS(status))
{
status = gckVIDMEM_Construct(
device->os,
physAddr,
device->contiguousSize,
64,
Args->bankSize,
&device->contiguousVidMem
);
if (gcmIS_SUCCESS(status))
{
gckALLOCATOR allocator = ((PLINUX_MDL)device->contiguousPhysical)->allocator;
device->contiguousVidMem->capability = allocator->capability | gcvALLOC_FLAG_MEMLIMIT;
device->contiguousVidMem->physical = device->contiguousPhysical;
device->contiguousBase = physAddr;
if (device->contiguousBase > 0xFFFFFFFFULL)
{
device->contiguousVidMem->capability &= ~gcvALLOC_FLAG_4GB_ADDR;
}
break;
}
gcmkONERROR(_FreeMemory(
device,
device->contiguousLogical,
device->contiguousPhysical
));
device->contiguousLogical = gcvNULL;
device->contiguousPhysical = gcvNULL;
}
if (device->contiguousSize <= (4 << 20))
{
device->contiguousSize = 0;
}
else
{
device->contiguousSize -= (4 << 20);
}
}
}
else
{
/* Create the contiguous memory heap. */
status = gckVIDMEM_Construct(
device->os,
Args->contiguousBase,
Args->contiguousSize,
64,
Args->bankSize,
&device->contiguousVidMem
);
if (gcmIS_ERROR(status))
{
/* Error, disable contiguous memory pool. */
device->contiguousVidMem = gcvNULL;
device->contiguousSize = 0;
}
else
{
gckALLOCATOR allocator;
gctBOOL contiguousRequested = Args->contiguousRequested;
#if gcdCAPTURE_ONLY_MODE
contiguousRequested = gcvTRUE;
#endif
gcmkONERROR(gckOS_RequestReservedMemory(
device->os, Args->contiguousBase, Args->contiguousSize,
"galcore contiguous memory",
contiguousRequested,
&device->contiguousPhysical
));
allocator = ((PLINUX_MDL)device->contiguousPhysical)->allocator;
device->contiguousVidMem->capability = allocator->capability | gcvALLOC_FLAG_MEMLIMIT;
device->contiguousVidMem->physical = device->contiguousPhysical;
device->requestedContiguousBase = Args->contiguousBase;
device->requestedContiguousSize = Args->contiguousSize;
device->contiguousPhysName = 0;
device->contiguousSize = Args->contiguousSize;
}
}
if (Args->showArgs)
{
gcmkPRINT("Galcore Info: ContiguousBase=0x%llx ContiguousSize=0x%zx\n", device->contiguousBase, device->contiguousSize);
}
OnError:
gcmkFOOTER();
return status;
}
static gceSTATUS
_SetupExternalSRAMVidMem(
IN gckGALDEVICE Device
)
{
gceSTATUS status = gcvSTATUS_OK;
gckGALDEVICE device = Device;
gctINT32 i, j = 0;
gcmkHEADER_ARG("Device=%p", Device);
/* Setup external SRAM memory region. */
for (i = 0; i < gcvSRAM_EXT_COUNT; i++)
{
if (!device->extSRAMSizes[i])
{
/* Keep this path for internal test, read from feature database. */
device->extSRAMSizes[i] = device->device->extSRAMSizes[i];
}
if (device->extSRAMSizes[i] > 0)
{
/* create the external SRAM memory heap */
status = gckVIDMEM_Construct(
device->os,
device->extSRAMBases[i],
device->extSRAMSizes[i],
64,
0,
&device->extSRAMVidMem[i]
);
if (gcmIS_ERROR(status))
{
/* Error, disable external SRAM heap. */
device->extSRAMSizes[i] = 0;
}
else
{
char sRAMName[40];
snprintf(sRAMName, gcmSIZEOF(sRAMName) - 1, "Galcore external sram%d", i);
#if gcdCAPTURE_ONLY_MODE
device->args.sRAMRequested = gcvTRUE;
#endif
/* Map external SRAM memory. */
gcmkONERROR(gckOS_RequestReservedMemory(
device->os,
device->extSRAMBases[i], device->extSRAMSizes[i],
sRAMName,
device->args.sRAMRequested,
&device->extSRAMPhysical[i]
));
device->extSRAMVidMem[i]->physical = device->extSRAMPhysical[i];
device->device->extSRAMPhysical[i] = device->extSRAMPhysical[i];
for (j = 0; j < gcdMAX_GPU_COUNT; j++)
{
if (device->irqLines[j] != -1 && device->kernels[j])
{
device->kernels[j]->hardware->options.extSRAMGPUPhysNames[i] = gckKERNEL_AllocateNameFromPointer(device->kernels[j], device->extSRAMPhysical[i]);
}
}
}
}
}
OnError:
gcmkFOOTER();
return status;
}
/******************************************************************************\
******************************* Interrupt Handler ******************************
\******************************************************************************/
static irqreturn_t isrRoutine(int irq, void *ctxt)
{
gceSTATUS status;
gckGALDEVICE device;
gceCORE core = (gceCORE)gcmPTR2INT32(ctxt) - 1;
device = galDevice;
/* Call kernel interrupt notification. */
status = gckHARDWARE_Interrupt(device->kernels[core]->hardware);
if (gcmIS_SUCCESS(status))
{
up(&device->semas[core]);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static irqreturn_t isrRoutineVG(int irq, void *ctxt)
{
return IRQ_NONE;
}
static const char *isrNames[] =
{
"galcore:0",
"galcore:3d-1",
"galcore:3d-2",
"galcore:3d-3",
"galcore:3d-4",
"galcore:3d-5",
"galcore:3d-6",
"galcore:3d-7",
"galcore:2d",
"galcore:vg",
#if gcdDEC_ENABLE_AHB
"galcore:dec"
#endif
};
static int isrRoutinePoll(void *ctxt)
{
gckGALDEVICE device;
gceCORE core = (gceCORE)gcmPTR2INT32(ctxt);
device = galDevice;
gcmSTATIC_ASSERT(gcvCORE_COUNT == gcmCOUNTOF(isrNames),
"isrNames array does not match core types");
while (1)
{
if (unlikely(device->killThread))
{
/* The daemon exits. */
while (!kthread_should_stop())
{
gckOS_Delay(device->os, 1);
}
return 0;
}
if (core == gcvCORE_VG)
{
isrRoutineVG(-1, gcvNULL);
}
else
{
isrRoutine(-1, (gctPOINTER)(uintptr_t)(core + 1));
}
gckOS_Delay(device->os, 10);
}
return 0;
}
static gceSTATUS
_SetupIsr(
IN gceCORE Core
)
{
gctINT ret = 0;
gceSTATUS status = gcvSTATUS_OK;
gckGALDEVICE Device = galDevice;
irq_handler_t handler;
gcmkHEADER_ARG("Device=%p Core=%d", Device, Core);
gcmkVERIFY_ARGUMENT(Device != NULL);
gcmSTATIC_ASSERT(gcvCORE_COUNT == gcmCOUNTOF(isrNames),
"isrNames array does not match core types");
if (Device->irqLines[Core] == -1)
{
gctUINT64 isrPolling = -1;
if (Device->isrThread[Core])
{
return status;
}
gckOS_QueryOption(Device->os, "isrPoll", &isrPolling);
/* use kthread to poll int stat */
if (gcmBITTEST(isrPolling, Core) != 0)
{
struct task_struct * task;
Device->killIsrThread = gcvFALSE;
task = kthread_run(isrRoutinePoll, (gctPOINTER)Core, "%s_poll", isrNames[Core]);
if (IS_ERR(task))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Could not start the intr poll thread.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_GENERIC_IO);
}
gcmkPRINT("galcore: polling core%d int state\n", Core);
Device->isrThread[Core] = task;
Device->isrInitializeds[Core] = gcvTRUE;
return status;
}
/* it should not run to here */
return gcvSTATUS_INVALID_ARGUMENT;
}
handler = (Core == gcvCORE_VG) ? isrRoutineVG : isrRoutine;
/*
* Hook up the isr based on the irq line.
* For shared irq, device-id can not be 0, but CORE_MAJOR value is.
* Add by 1 here and subtract by 1 in isr to fix the issue.
*/
ret = request_irq(
Device->irqLines[Core], handler, gcdIRQF_FLAG,
isrNames[Core], (void *)(uintptr_t)(Core + 1)
);
if (ret != 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Could not register irq line %d (error=%d)\n",
__FUNCTION__, __LINE__,
Device->irqLines[Core], ret
);
gcmkONERROR(gcvSTATUS_GENERIC_IO);
}
/* Mark ISR as initialized. */
Device->isrInitializeds[Core] = gcvTRUE;
OnError:
gcmkFOOTER();
return status;
}
static gceSTATUS
_ReleaseIsr(
IN gceCORE Core
)
{
gckGALDEVICE Device = galDevice;
gcmkHEADER_ARG("Device=%p Core=%d", Device, Core);
gcmkVERIFY_ARGUMENT(Device != NULL);
/* release the irq */
if (Device->isrInitializeds[Core])
{
if (Device->isrThread[Core])
{
Device->killIsrThread = gcvTRUE;
kthread_stop(Device->isrThread[Core]);
Device->isrThread[Core] = gcvNULL;
}
else
{
free_irq(Device->irqLines[Core], (void *)(uintptr_t)(Core + 1));
}
Device->isrInitializeds[Core] = gcvFALSE;
}
gcmkFOOTER_NO();
return gcvSTATUS_OK;
}
static int threadRoutine(void *ctxt)
{
gckGALDEVICE device = galDevice;
gceCORE core = (gceCORE) gcmPTR2INT32(ctxt);
gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
"Starting isr Thread with extension=%p",
device);
for (;;)
{
int down;
down = down_interruptible(&device->semas[core]);
if (down && down != -EINTR)
{
return down;
}
if (unlikely(device->killThread))
{
/* The daemon exits. */
while (!kthread_should_stop())
{
gckOS_Delay(device->os, 1);
}
return 0;
}
gckKERNEL_Notify(device->kernels[core], gcvNOTIFY_INTERRUPT);
}
}
static gceSTATUS
_StartThread(
IN gckGALDEVICE Device,
IN gceCORE Core
)
{
gceSTATUS status = gcvSTATUS_OK;
gckGALDEVICE device = galDevice;
struct task_struct * task;
if (device->kernels[Core] != gcvNULL)
{
/* Start the kernel thread. */
task = kthread_run(threadRoutine, (void *)Core,
"galcore_deamon/%d", Core);
if (IS_ERR(task))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Could not start the kernel thread.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_GENERIC_IO);
}
device->threadCtxts[Core] = task;
device->threadInitializeds[Core] = device->kernels[Core]->threadInitialized = gcvTRUE;
/* Set highest non RT priority, same as work queues. */
set_user_nice(task, MIN_NICE);
}
else
{
device->threadInitializeds[Core] = gcvFALSE;
}
OnError:
return status;
}
static void
_StopThread(
gckGALDEVICE Device,
gceCORE Core
)
{
if (Device->threadInitializeds[Core])
{
Device->killThread = gcvTRUE;
up(&Device->semas[Core]);
kthread_stop(Device->threadCtxts[Core]);
Device->threadCtxts[Core] = gcvNULL;
Device->threadInitializeds[Core] = gcvFALSE;
}
}
/*******************************************************************************
**
** gckGALDEVICE_Construct
**
** Constructor.
**
** INPUT:
**
** OUTPUT:
**
** gckGALDEVICE * Device
** Pointer to a variable receiving the gckGALDEVICE object pointer on
** success.
*/
gceSTATUS
gckGALDEVICE_Construct(
IN gcsPLATFORM * Platform,
IN const gcsMODULE_PARAMETERS * Args,
OUT gckGALDEVICE *Device
)
{
gckKERNEL kernel = gcvNULL;
gckGALDEVICE device;
gctINT32 i;
#if !gcdCAPTURE_ONLY_MODE
gceHARDWARE_TYPE type;
#endif
gceSTATUS status = gcvSTATUS_OK;
gctUINT64 isrPolling = -1;
gcmkHEADER_ARG("Platform=%p Args=%p", Platform, Args);
/* Allocate device structure. */
device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL | __GFP_NOWARN);
if (!device)
{
gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
}
memset(device, 0, sizeof(struct _gckGALDEVICE));
device->platform = Platform;
device->platform->dev = gcvNULL;
device->args = *Args;
/* Clear irq lines. */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
device->irqLines[i] = -1;
#if USE_LINUX_PCIE
device->bars[i] = -1;
#endif
}
for (i = 0; i < gcvCORE_COUNT; i++)
{
device->irqLines[i] = Args->irqs[i];
device->requestedRegisterMemBases[i] = Args->registerBases[i];
device->requestedRegisterMemSizes[i] = Args->registerSizes[i];
#if USE_LINUX_PCIE
device->bars[i] = Args->bars[i];
#endif
gcmkTRACE_ZONE(gcvLEVEL_INFO, _GC_OBJ_ZONE,
"Get register base %llx of core %d",
Args->registerBases[i], i);
}
device->requestedContiguousBase = 0;
device->requestedContiguousSize = 0;
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
unsigned long physical;
physical = (unsigned long)device->requestedRegisterMemBases[i];
/* Set up register memory region. */
if (physical != 0)
{
if (Args->registerBasesMapped[i])
{
device->registerBases[i] = Args->registerBasesMapped[i];
device->requestedRegisterMemBases[i] = 0;
}
else
{
#if USE_LINUX_PCIE
gcmkPRINT("register should be mapped in platform layer");
#endif
if (!request_mem_region(physical,
device->requestedRegisterMemSizes[i],
"galcore register region"))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Failed to claim %lu bytes @ 0x%llx\n",
__FUNCTION__, __LINE__,
device->requestedRegisterMemSizes[i], physical
);
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0) && (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
device->registerBases[i] = (gctPOINTER)ioremap(
#else
device->registerBases[i] = (gctPOINTER)ioremap_nocache(
#endif
physical, device->requestedRegisterMemSizes[i]);
if (device->registerBases[i] == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Unable to map %ld bytes @ 0x%zx\n",
__FUNCTION__, __LINE__,
physical, device->requestedRegisterMemSizes[i]
);
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
}
}
}
}
/* Set the base address */
device->baseAddress = device->physBase = Args->baseAddress;
device->physSize = Args->physSize;
/* Set the external base address */
device->externalBase = Args->externalBase;
device->externalSize = Args->externalSize;
for (i = 0; i < gcvSRAM_EXT_COUNT; i++)
{
device->extSRAMBases[i] = Args->extSRAMBases[i];
device->extSRAMSizes[i] = Args->extSRAMSizes[i];
}
/* Ensure root debugfs dir is created before allocator init. */
gcmkONERROR(_DebugfsInit(device));
/* Construct the gckOS object. */
gcmkONERROR(gckOS_Construct(device, &device->os));
if (device->externalSize > 0)
{
/* create the external memory heap */
status = gckVIDMEM_Construct(
device->os,
device->externalBase,
device->externalSize,
64,
0,
&device->externalVidMem
);
if (gcmIS_ERROR(status))
{
/* Error, disable external heap. */
device->externalSize = 0;
}
else
{
/* Map external memory. */
gcmkONERROR(gckOS_RequestReservedMemory(
device->os,
device->externalBase, device->externalSize,
"galcore external memory",
gcvTRUE,
&device->externalPhysical
));
device->externalVidMem->physical = device->externalPhysical;
}
}
/* Construct the gckDEVICE object for os independent core management. */
gcmkONERROR(gckDEVICE_Construct(device->os, &device->device));
device->device->showSRAMMapInfo = Args->showArgs;
device->platform->dev = device->device;
gckOS_QueryOption(device->os, "isrPoll", &isrPolling);
if (device->irqLines[gcvCORE_MAJOR] != -1 || gcmBITTEST(isrPolling, gcvCORE_MAJOR)!= 0)
{
gcmkONERROR(gctaOS_ConstructOS(device->os, &device->taos));
}
/* Setup contiguous video memory pool. */
gcmkONERROR(_SetupContiguousVidMem(device, Args));
#if gcdEXTERNAL_SRAM_DEFAULT_POOL
/* Setup external SRAM video memory pool. */
gcmkONERROR(_SetupExternalSRAMVidMem(device));
#endif
/* Add core for all available major cores. */
for (i = gcvCORE_MAJOR; i <= gcvCORE_3D_MAX; i++)
{
if (device->irqLines[i] != -1 || gcmBITTEST(isrPolling, i)!= 0)
{
gcmkONERROR(gcTA_Construct(
device->taos,
(gceCORE)i,
&globalTA[i]
));
gcmkONERROR(gckDEVICE_AddCore(
device->device,
(gceCORE)i,
Args->chipIDs[i],
device,
&device->kernels[i]
));
gcmkONERROR(gckHARDWARE_SetFastClear(
device->kernels[i]->hardware,
Args->fastClear,
Args->compression
));
gcmkONERROR(gckHARDWARE_EnablePowerManagement(
device->kernels[i]->hardware,
Args->powerManagement
));
#if gcdENABLE_FSCALE_VAL_ADJUST
gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
device->kernels[i]->hardware,
Args->gpu3DMinClock
));
#endif
gcmkONERROR(gckHARDWARE_SetGpuProfiler(
device->kernels[i]->hardware,
Args->gpuProfiler
));
}
else
{
device->kernels[i] = gcvNULL;
}
}
#if !gcdCAPTURE_ONLY_MODE
if (device->irqLines[gcvCORE_2D] != -1 || gcmBITTEST(isrPolling, gcvCORE_2D)!= 0)
{
gcmkONERROR(gckDEVICE_AddCore(
device->device,
gcvCORE_2D,
gcvCHIP_ID_DEFAULT,
device,
&device->kernels[gcvCORE_2D]
));
/* Verify the hardware type */
gcmkONERROR(gckHARDWARE_GetType(
device->kernels[gcvCORE_2D]->hardware,
&type
));
if (type != gcvHARDWARE_2D)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Unexpected hardware type: %d\n",
__FUNCTION__, __LINE__,
type
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
gcmkONERROR(gckHARDWARE_EnablePowerManagement(
device->kernels[gcvCORE_2D]->hardware,
Args->powerManagement
));
#if gcdENABLE_FSCALE_VAL_ADJUST
gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
device->kernels[gcvCORE_2D]->hardware, 1
));
#endif
}
else
{
device->kernels[gcvCORE_2D] = gcvNULL;
}
if (device->irqLines[gcvCORE_VG] != -1 || gcmBITTEST(isrPolling, gcvCORE_VG)!= 0)
{
}
else
{
device->kernels[gcvCORE_VG] = gcvNULL;
}
#else
device->kernels[gcvCORE_2D] = gcvNULL;
device->kernels[gcvCORE_VG] = gcvNULL;
#endif
#if !gcdEXTERNAL_SRAM_DEFAULT_POOL
/* Setup external SRAM video memory pool. */
gcmkONERROR(_SetupExternalSRAMVidMem(device));
#endif
/* Initialize the kernel thread semaphores. */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if ((device->irqLines[i] != -1 || gcmBITTEST(isrPolling, i)!= 0)
&& device->kernels[i])
{
sema_init(&device->semas[i], 0);
}
}
/* Grab the first valid kernel. */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
kernel = device->kernels[i];
break;
}
}
if (!kernel)
{
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
if (device->internalPhysical)
{
device->internalPhysName = gcmPTR_TO_NAME(device->internalPhysical);
}
if (device->externalPhysical)
{
device->externalPhysName = gcmPTR_TO_NAME(device->externalPhysical);
}
if (device->contiguousPhysical)
{
device->contiguousPhysName = gcmPTR_TO_NAME(device->contiguousPhysical);
}
/* Return pointer to the device. */
*Device = galDevice = device;
OnError:
if (gcmIS_ERROR(status))
{
/* Roll back. */
gcmkVERIFY_OK(gckGALDEVICE_Destroy(device));
}
gcmkFOOTER();
return status;
}
/*******************************************************************************
**
** gckGALDEVICE_Destroy
**
** Class destructor.
**
** INPUT:
**
** Nothing.
**
** OUTPUT:
**
** Nothing.
**
** RETURNS:
**
** Nothing.
*/
gceSTATUS
gckGALDEVICE_Destroy(
gckGALDEVICE Device)
{
gctINT i, j = 0;
gckKERNEL kernel = gcvNULL;
gcmkHEADER_ARG("Device=%p", Device);
if (Device != gcvNULL)
{
/* Grab the first available kernel */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (Device->kernels[i])
{
kernel = Device->kernels[i];
break;
}
}
if (kernel)
{
if (Device->internalPhysName != 0)
{
gcmRELEASE_NAME(Device->internalPhysName);
Device->internalPhysName = 0;
}
if (Device->externalPhysName != 0)
{
gcmRELEASE_NAME(Device->externalPhysName);
Device->externalPhysName = 0;
}
if (Device->contiguousPhysName != 0)
{
gcmRELEASE_NAME(Device->contiguousPhysName);
Device->contiguousPhysName = 0;
}
}
/* Destroy per-core SRAM heap. */
for (i = 0; i < gcvCORE_COUNT; i++)
{
if (Device->kernels[i])
{
kernel = Device->kernels[i];
for (j = gcvSRAM_INTERNAL0; j < gcvSRAM_INTER_COUNT; j++)
{
if (kernel->sRAMPhysical[j] != gcvNULL)
{
/* Release reserved SRAM memory. */
gckOS_ReleaseReservedMemory(
Device->os,
kernel->sRAMPhysical[j]
);
kernel->sRAMPhysical[j] = gcvNULL;
}
if (kernel->sRAMVidMem[j] != gcvNULL)
{
/* Destroy the SRAM contiguous heap. */
gcmkVERIFY_OK(gckVIDMEM_Destroy(kernel->sRAMVidMem[j]));
kernel->sRAMVidMem[j] = gcvNULL;
}
}
}
}
if (Device->device)
{
gcmkVERIFY_OK(gckDEVICE_Destroy(Device->os, Device->device));
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (globalTA[i])
{
gcTA_Destroy(globalTA[i]);
globalTA[i] = gcvNULL;
}
}
Device->device = gcvNULL;
}
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (Device->kernels[i] != gcvNULL)
{
Device->kernels[i] = gcvNULL;
}
}
if (Device->internalLogical != gcvNULL)
{
/* Unmap the internal memory. */
iounmap(Device->internalLogical);
Device->internalLogical = gcvNULL;
}
if (Device->internalVidMem != gcvNULL)
{
/* Destroy the internal heap. */
gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
Device->internalVidMem = gcvNULL;
}
for (i = 0; i < gcvSRAM_EXT_COUNT; i++)
{
if (Device->extSRAMPhysical[i] != gcvNULL)
{
gckOS_ReleaseReservedMemory(
Device->os,
Device->extSRAMPhysical[i]
);
Device->extSRAMPhysical[i] = gcvNULL;
}
if (Device->extSRAMVidMem[i] != gcvNULL)
{
gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->extSRAMVidMem[i]));
Device->extSRAMVidMem[i] = gcvNULL;
}
}
if (Device->externalPhysical != gcvNULL)
{
gckOS_ReleaseReservedMemory(
Device->os,
Device->externalPhysical
);
Device->externalPhysical = gcvNULL;
}
if (Device->externalLogical != gcvNULL)
{
Device->externalLogical = gcvNULL;
}
if (Device->externalVidMem != gcvNULL)
{
/* destroy the external heap */
gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
Device->externalVidMem = gcvNULL;
}
/*
* Destroy contiguous memory pool after gckDEVICE destroyed. gckDEVICE
* may allocates GPU memory types from SYSTEM pool.
*/
if (Device->contiguousPhysical != gcvNULL)
{
if (Device->requestedContiguousBase == 0)
{
gcmkVERIFY_OK(_FreeMemory(
Device,
Device->contiguousLogical,
Device->contiguousPhysical
));
}
else
{
gckOS_ReleaseReservedMemory(
Device->os,
Device->contiguousPhysical
);
Device->contiguousPhysical = gcvNULL;
Device->requestedContiguousBase = 0;
Device->requestedContiguousSize = 0;
}
Device->contiguousLogical = gcvNULL;
Device->contiguousPhysical = gcvNULL;
}
if (Device->contiguousVidMem != gcvNULL)
{
/* Destroy the contiguous heap. */
gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
Device->contiguousVidMem = gcvNULL;
}
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (Device->registerBases[i])
{
/* Unmap register memory. */
if (Device->requestedRegisterMemBases[i] != 0)
{
iounmap(Device->registerBases[i]);
release_mem_region(Device->requestedRegisterMemBases[i],
Device->requestedRegisterMemSizes[i]);
}
Device->registerBases[i] = gcvNULL;
Device->requestedRegisterMemBases[i] = 0;
Device->requestedRegisterMemSizes[i] = 0;
}
}
if (Device->taos)
{
gcmkVERIFY_OK(gctaOS_DestroyOS(Device->taos));
Device->taos = gcvNULL;
}
/* Destroy the gckOS object. */
if (Device->os != gcvNULL)
{
gcmkVERIFY_OK(gckOS_Destroy(Device->os));
Device->os = gcvNULL;
}
_DebugfsCleanup(Device);
/* Free the device. */
kfree(Device);
}
gcmkFOOTER_NO();
return gcvSTATUS_OK;
}
/*******************************************************************************
**
** gckGALDEVICE_Start
**
** Start the gal device, including the following actions: setup the isr routine
** and start the daemoni thread.
**
** INPUT:
**
** gckGALDEVICE Device
** Pointer to an gckGALDEVICE object.
**
** OUTPUT:
**
** Nothing.
**
** RETURNS:
**
** gcvSTATUS_OK
** Start successfully.
*/
gceSTATUS
gckGALDEVICE_Start(
IN gckGALDEVICE Device
)
{
gctUINT i;
gceSTATUS status = gcvSTATUS_OK;
gcmkHEADER_ARG("Device=%p", Device);
/* Start the kernel threads. */
for (i = 0; i < gcvCORE_COUNT; ++i)
{
if (i == gcvCORE_VG)
{
continue;
}
gcmkONERROR(_StartThread(Device, i));
}
for (i = 0; i < gcvCORE_COUNT; i++)
{
if (Device->kernels[i] == gcvNULL)
{
continue;
}
/* Setup the ISR routine. */
gcmkONERROR(_SetupIsr(i));
if (i == gcvCORE_VG)
{
}
else
{
/* Switch to SUSPEND power state. */
gcmkONERROR(gckHARDWARE_SetPowerState(
Device->kernels[i]->hardware, gcvPOWER_OFF_BROADCAST
));
gcmkONERROR(gckHARDWARE_StartTimerReset(Device->kernels[i]->hardware));
}
}
OnError:
gcmkFOOTER();
return status;
}
/*******************************************************************************
**
** gckGALDEVICE_Stop
**
** Stop the gal device, including the following actions: stop the daemon
** thread, release the irq.
**
** INPUT:
**
** gckGALDEVICE Device
** Pointer to an gckGALDEVICE object.
**
** OUTPUT:
**
** Nothing.
**
** RETURNS:
**
** Nothing.
*/
gceSTATUS
gckGALDEVICE_Stop(
gckGALDEVICE Device
)
{
gctUINT i;
gceSTATUS status = gcvSTATUS_OK;
gcmkHEADER_ARG("Device=%p", Device);
gcmkVERIFY_ARGUMENT(Device != NULL);
for (i = 0; i < gcvCORE_COUNT; i++)
{
if (Device->kernels[i] == gcvNULL)
{
continue;
}
if (i == gcvCORE_VG)
{
}
else
{
gcmkONERROR(gckHARDWARE_EnablePowerManagement(
Device->kernels[i]->hardware, gcvTRUE
));
/* Switch to OFF power state. */
gcmkONERROR(gckHARDWARE_SetPowerState(
Device->kernels[i]->hardware, gcvPOWER_OFF
));
gckHARDWARE_StartTimerReset(Device->kernels[i]->hardware);
}
/* Stop the ISR routine. */
gcmkONERROR(_ReleaseIsr(i));
}
/* Stop the kernel thread. */
for (i = 0; i < gcvCORE_COUNT; i++)
{
_StopThread(Device, i);
}
OnError:
gcmkFOOTER();
return status;
}