/**************************************************************************** * * The MIT License (MIT) * * Copyright (c) 2014 - 2018 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 - 2018 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 #include #include #include #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; gctUINT32 chipRevision; gctUINT32 productID = 0; gctUINT32 ecoID = 0; if (!device) return -ENXIO; for (i = 0; i < gcdMAX_GPU_COUNT; i++) { if (device->irqLines[i] != -1) { #if gcdENABLE_VG if (i == gcvCORE_VG) { chipModel = device->kernels[i]->vg->hardware->chipModel; chipRevision = device->kernels[i]->vg->hardware->chipRevision; } else #endif { 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; } static void _CounterAdd( gcsDATABASE_COUNTERS * Dest, gcsDATABASE_COUNTERS * Src ) { Dest->bytes += Src->bytes; Dest->maxBytes += Src->maxBytes; Dest->totalBytes += Src->totalBytes; } static void _CounterPrint( gcsDATABASE_COUNTERS * Counter, gctCONST_STRING Name, struct seq_file* m ) { seq_printf(m, " %s:\n", Name); seq_printf(m, " Used : %10llu B\n", Counter->bytes); } 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 contiguousCounter = {0, 0, 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, " gcvPOOL_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 = &database->vidMemPool[gcvPOOL_CONTIGUOUS]; _CounterAdd(&contiguousCounter, counter); counter = &database->vidMemPool[gcvPOOL_VIRTUAL]; _CounterAdd(&virtualCounter, counter); counter = &database->nonPaged; _CounterAdd(&nonPagedCounter, counter); } } /* Release the database mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex)); _CounterPrint(&contiguousCounter, "gcvPOOL_CONTIGUOUS", m); _CounterPrint(&virtualCounter, "gcvPOOL_VIRTUAL", m); seq_printf(m, "\n"); seq_printf(m, "NON PAGED MEMORY:\n"); seq_printf(m, " Used : %10llu B\n", nonPagedCounter.bytes); return 0; } static int _ShowRecord( IN struct seq_file *File, IN gcsDATABASE_RECORD_PTR Record ) { static const char * recordTypes[gcvDB_NUM_TYPES] = { "Unknown", "VideoMemory", "CommandBuffer", "NonPaged", "Contiguous", "Signal", "VidMemLock", "Context", "Idel", "MapMemory", "MapUserMemory", "ShBuf", }; seq_printf(File, "%-14s %3d %16p %16zu %16zu\n", recordTypes[Record->type], Record->kernel->core, Record->data, (size_t) Record->physical, Record->bytes ); return 0; } static int _ShowRecords( IN struct seq_file *File, IN gcsDATABASE_PTR Database ) { gctUINT i; 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, record); record = record->next; } } return 0; } static void _ShowCounters( struct seq_file *File, gcsDATABASE_PTR Database ) { gctUINT i = 0; static const char * surfaceTypes[gcvSURF_NUM_TYPES] = { "Unknown", "Index", "Vertex", "Texture", "RenderTarget", "Depth", "Bitmap", "TileStatus", "Image", "Mask", "Scissor", "HZ", "ICache", "TxDesc", "Fence", "TFBHeader", }; static const char * poolTypes[gcvPOOL_NUMBER_OF_POOLS] = { "Unknown", "Default", "Local", "Internal", "External", "Unified", "System", "Virtual", "User", "Contiguous", }; 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 < gcvSURF_NUM_TYPES; i++) { seq_printf(File, "%-16s %16lld %16lld %16lld\n", surfaceTypes[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", poolTypes[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 void _ShowProcess( IN struct seq_file *File, IN gcsDATABASE_PTR Database ) { gctINT pid; 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); /* Detailed records */ _ShowRecords(File, Database); seq_printf(File, "Counters:\n"); _ShowCounters(File, Database); } static void _ShowProcesses( IN struct seq_file * File, IN gckKERNEL Kernel ) { gcsDATABASE_PTR database; gctINT i; static gctUINT64 idleTime = 0; /* 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(File, "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) { _ShowProcess(File, database); } } /* Release the database mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); } static int gc_db_show(struct seq_file *m, void *data) { gcsINFO_NODE *node = m->private; gckGALDEVICE device = node->device; gckKERNEL kernel = _GetValidKernel(device); if (!kernel) return -ENXIO; _ShowProcesses(m, kernel); 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 ; } /******************************************************************************* ** ** 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); gctUINT64 start; gctUINT64 end; gctUINT64 on; gctUINT64 off; gctUINT64 idle; gctUINT64 suspend; if (!kernel) return -ENXIO; gckHARDWARE_QueryStateTimer(kernel->hardware, &start, &end, &on, &off, &idle, &suspend); /* Idle time since last call */ seq_printf(m, "Start: %llu ns\n", start); seq_printf(m, "End: %llu ns\n", end); seq_printf(m, "On: %llu ns\n", on); seq_printf(m, "Off: %llu ns\n", off); seq_printf(m, "Idle: %llu ns\n", idle); seq_printf(m, "Suspend: %llu ns\n", suspend); 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 || gcdENABLE_2D 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 || gcdENABLE_2D 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 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 %d: %s):\n", database->processID, name); _ShowCounters(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); _ShowCounters(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; for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++) { if (device->kernels[i]) { gckHARDWARE hardware = device->kernels[i]->hardware; #if gcdENABLE_VG if (i == gcvCORE_VG) { continue; } #endif 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}, {"version", gc_version_show}, {"vidmem", 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; gckDEBUGFS_DIR dir = &Device->debugfsDir; gcmkONERROR(gckDEBUGFS_DIR_Init(dir, gcvNULL, "gc")); gcmkONERROR(gckDEBUGFS_DIR_CreateFiles(dir, InfoList, gcmCOUNTOF(InfoList), Device)); return gcvSTATUS_OK; 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 gctUINT32 *PhysAddr ) { gceSTATUS status; gctPHYS_ADDR_T physAddr; gcmkHEADER_ARG("Device=0x%x Bytes=%lu", Device, Bytes); gcmkVERIFY_ARGUMENT(Device != NULL); gcmkVERIFY_ARGUMENT(Logical != NULL); gcmkVERIFY_ARGUMENT(Physical != NULL); gcmkVERIFY_ARGUMENT(PhysAddr != NULL); gcmkONERROR(gckOS_AllocateContiguous( Device->os, gcvFALSE, &Bytes, Physical, Logical )); gcmkONERROR(gckOS_GetPhysicalAddress( Device->os, *Logical, &physAddr )); gcmkSAFECASTPHYSADDRT(*PhysAddr, physAddr); /* Success. */ gcmkFOOTER_ARG( "*Logical=0x%x *Physical=0x%x *PhysAddr=0x%08x", *Logical, *Physical, *PhysAddr ); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return status; } static gceSTATUS _FreeMemory( IN gckGALDEVICE Device, IN gctPOINTER Logical, IN gctPHYS_ADDR Physical) { gceSTATUS status; gcmkHEADER_ARG("Device=0x%x Logical=0x%x Physical=0x%x", Device, Logical, Physical); gcmkVERIFY_ARGUMENT(Device != NULL); status = gckOS_FreeContiguous( Device->os, Physical, Logical, ((PLINUX_MDL) Physical)->numPages * PAGE_SIZE ); gcmkFOOTER(); return status; } static gceSTATUS _SetupVidMem( IN gckGALDEVICE Device, IN gctUINT32 ContiguousBase, IN gctSIZE_T ContiguousSize, IN gctSIZE_T BankSize, IN gcsDEVICE_CONSTRUCT_ARGS * Args ) { gceSTATUS status; gctUINT32 physAddr = ~0U; gckGALDEVICE device = Device; /* set up the contiguous memory */ device->contiguousBase = ContiguousBase; device->contiguousSize = ContiguousSize; if (ContiguousSize > 0) { if (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->systemMemoryBaseAddress, device->contiguousSize, 64, BankSize, &device->contiguousVidMem ); if (gcmIS_SUCCESS(status)) { device->contiguousVidMem->physical = device->contiguousPhysical; device->contiguousBase = physAddr; 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, ContiguousBase | device->systemMemoryBaseAddress, ContiguousSize, 64, BankSize, &device->contiguousVidMem ); if (gcmIS_ERROR(status)) { /* Error, disable contiguous memory pool. */ device->contiguousVidMem = gcvNULL; device->contiguousSize = 0; } else { gcmkONERROR(gckOS_RequestReservedMemory( device->os, ContiguousBase, ContiguousSize, "galcore contiguous memory", Args->contiguousRequested, &device->contiguousPhysical )); device->contiguousVidMem->physical = device->contiguousPhysical; device->requestedContiguousBase = ContiguousBase; device->requestedContiguousSize = ContiguousSize; device->contiguousPhysicalName = 0; device->contiguousSize = ContiguousSize; } } } return gcvSTATUS_OK; OnError: return status; } void _SetupRegisterPhysical( IN gckGALDEVICE Device, IN gcsDEVICE_CONSTRUCT_ARGS * Args ) { gctINT *irqs = Args->irqs; gctUINT *registerBases = Args->registerBases; gctUINT *registerSizes = Args->registerSizes; gctINT i = 0; for (i = 0; i < gcvCORE_COUNT; i++) { if (irqs[i] != -1) { Device->requestedRegisterMemBases[i] = registerBases[i]; Device->requestedRegisterMemSizes[i] = registerSizes[i]; gcmkTRACE_ZONE(gcvLEVEL_INFO, _GC_OBJ_ZONE, "Get register base %llx of core %d", registerBases[i], i); } } } /******************************************************************************\ ******************************* 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 = gckKERNEL_Notify(device->kernels[core], gcvNOTIFY_INTERRUPT, gcvTRUE); if (gcmIS_SUCCESS(status)) { up(&device->semas[core]); return IRQ_HANDLED; } return IRQ_NONE; } static int threadRoutine(void *ctxt) { gckGALDEVICE device = galDevice; gceCORE core = (gceCORE) gcmPTR2INT32(ctxt); gctUINT i; gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, "Starting isr Thread with extension=%p", device); if (core != gcvCORE_VG) { /* Make kernel update page table of this thread to include entry related to command buffer.*/ for (i = 0; i < gcdCOMMAND_QUEUES; i++) { gctUINT32 data = *(gctUINT32_PTR)device->kernels[core]->command->queues[i].logical; data = 0; } } for (;;) { static int down; down = down_interruptible(&device->semas[core]); if (down); /*To make gcc 4.6 happye*/ if (device->killThread == gcvTRUE) { /* The daemon exits. */ while (!kthread_should_stop()) { gckOS_Delay(device->os, 1); } return 0; } gckKERNEL_Notify(device->kernels[core], gcvNOTIFY_INTERRUPT, gcvFALSE); } } static irqreturn_t isrRoutineVG(int irq, void *ctxt) { #if gcdENABLE_VG gceSTATUS status; gckGALDEVICE device; device = (gckGALDEVICE) ctxt; /* Serve the interrupt. */ status = gckVGINTERRUPT_Enque(device->kernels[gcvCORE_VG]->vg->interrupt); /* Determine the return value. */ return (status == gcvSTATUS_NOT_OUR_INTERRUPT) ? IRQ_RETVAL(0) : IRQ_RETVAL(1); #else return IRQ_NONE; #endif } /******************************************************************************\ ******************************* gckGALDEVICE Code ****************************** \******************************************************************************/ static gceSTATUS _StartThread( IN int (*ThreadRoutine)(void *data), IN gceCORE Core ) { gceSTATUS status; gckGALDEVICE device = galDevice; struct task_struct * task; if (device->kernels[Core] != gcvNULL) { /* Start the kernel thread. */ task = kthread_run(ThreadRoutine, (void *)Core, "galcore deamon thread for core[%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] = gcvTRUE; } else { device->threadInitializeds[Core] = gcvFALSE; } return gcvSTATUS_OK; OnError: return status; } /******************************************************************************* ** ** gckGALDEVICE_Construct ** ** Constructor. ** ** INPUT: ** ** OUTPUT: ** ** gckGALDEVICE * Device ** Pointer to a variable receiving the gckGALDEVICE object pointer on ** success. */ gceSTATUS gckGALDEVICE_Construct( IN gctINT IrqLine, IN gctUINT32 RegisterMemBase, IN gctSIZE_T RegisterMemSize, IN gctINT IrqLine2D, IN gctUINT32 RegisterMemBase2D, IN gctSIZE_T RegisterMemSize2D, IN gctINT IrqLineVG, IN gctUINT32 RegisterMemBaseVG, IN gctSIZE_T RegisterMemSizeVG, IN gctUINT32 ContiguousBase, IN gctSIZE_T ContiguousSize, IN gctUINT32 ExternalBase, IN gctSIZE_T ExternalSize, IN gctSIZE_T BankSize, IN gctINT FastClear, IN gctINT Compression, IN gctUINT32 PhysBaseAddr, IN gctUINT32 PhysSize, IN gctINT Signal, IN gctUINT LogFileSize, IN gctINT PowerManagement, IN gctINT GpuProfiler, IN gcsDEVICE_CONSTRUCT_ARGS * Args, OUT gckGALDEVICE *Device ) { gctUINT32 internalBaseAddress = 0, internalAlignment = 0; gctUINT32 externalAlignment = 0; gctUINT32 physical; gckGALDEVICE device; gceSTATUS status; gctINT32 i; gceHARDWARE_TYPE type; gckKERNEL kernel = gcvNULL; gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u " "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u " "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u " "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu " "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d", IrqLine, RegisterMemBase, RegisterMemSize, IrqLine2D, RegisterMemBase2D, RegisterMemSize2D, IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG, ContiguousBase, ContiguousSize, BankSize, FastClear, Compression, PhysBaseAddr, PhysSize, Signal); #if !gcdENABLE_3D IrqLine = -1; #endif #if !gcdENABLE_2D IrqLine2D = -1; #endif /* 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->dbgNode = gcvNULL; device->platform = Args->platform; device->args = *Args; /* set up the contiguous memory */ device->contiguousSize = ContiguousSize; /* Clear irq lines. */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { device->irqLines[i] = -1; } _SetupRegisterPhysical(device, Args); if (IrqLine != -1) { device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase; device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize; } if (IrqLine2D != -1) { device->requestedRegisterMemBases[gcvCORE_2D] = RegisterMemBase2D; device->requestedRegisterMemSizes[gcvCORE_2D] = RegisterMemSize2D; } if (IrqLineVG != -1) { device->requestedRegisterMemBases[gcvCORE_VG] = RegisterMemBaseVG; device->requestedRegisterMemSizes[gcvCORE_VG] = RegisterMemSizeVG; } #if gcdDEC_ENABLE_AHB { device->requestedRegisterMemBases[gcvCORE_DEC] = Args->registerMemBaseDEC300; device->requestedRegisterMemSizes[gcvCORE_DEC] = Args->registerMemSizeDEC300; } #endif for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++) { if (Args->irqs[i] != -1) { device->requestedRegisterMemBases[i] = Args->registerBases[i]; device->requestedRegisterMemSizes[i] = Args->registerSizes[i]; gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DEVICE, "%s(%d): Core = %d, RegiseterBase = %x", __FUNCTION__, __LINE__, i, Args->registerBases[i] ); } } /* Initialize the ISR. */ device->irqLines[gcvCORE_MAJOR] = IrqLine; device->irqLines[gcvCORE_2D] = IrqLine2D; device->irqLines[gcvCORE_VG] = IrqLineVG; for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++) { if (Args->irqs[i] != -1) { device->irqLines[i] = Args->irqs[i]; } } device->requestedContiguousBase = 0; device->requestedContiguousSize = 0; for (i = 0; i < gcdMAX_GPU_COUNT; i++) { physical = device->requestedRegisterMemBases[i]; /* Set up register memory region. */ if (physical != 0) { if (Args->registerMemMapped) { device->registerBases[i] = Args->registerMemAddress; device->requestedRegisterMemBases[i] = 0; } else { #if USE_LINUX_PCIE device->registerBases[i] = (gctPOINTER) pci_iomap(device->platform->device, 1, device->requestedRegisterMemSizes[i]); #else 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%zx\n", __FUNCTION__, __LINE__, physical, device->requestedRegisterMemSizes[i] ); gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } device->registerBases[i] = (gctPOINTER)ioremap_nocache( physical, device->requestedRegisterMemSizes[i]); #endif if (device->registerBases[i] == gcvNULL) { gcmkTRACE_ZONE( gcvLEVEL_ERROR, gcvZONE_DRIVER, "%s(%d): Unable to map %ld bytes @ 0x%08X\n", __FUNCTION__, __LINE__, physical, device->requestedRegisterMemSizes[i] ); gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } } physical += device->requestedRegisterMemSizes[i]; } } /* Set the base address */ device->baseAddress = device->physBase = PhysBaseAddr; device->physSize = PhysSize; /* Construct the gckOS object. */ gcmkONERROR(gckOS_Construct(device, &device->os)); /* Construct the gckDEVICE object for os independent core management. */ gcmkONERROR(gckDEVICE_Construct(device->os, &device->device)); if (device->irqLines[gcvCORE_MAJOR] != -1) { gcmkONERROR(gctaOS_ConstructOS(device->os, &device->taos)); } gcmkONERROR(_SetupVidMem(device, ContiguousBase, ContiguousSize, BankSize, Args)); /* Set external base and size */ device->externalBase = ExternalBase; device->externalSize = ExternalSize; if (device->irqLines[gcvCORE_MAJOR] != -1) { gcmkONERROR(gcTA_Construct(device->taos, gcvCORE_MAJOR, &globalTA[gcvCORE_MAJOR])); gcmkONERROR(gckDEVICE_AddCore(device->device, gcvCORE_MAJOR, Args->chipIDs[gcvCORE_MAJOR], device, &device->kernels[gcvCORE_MAJOR])); gcmkONERROR(gckHARDWARE_SetFastClear( device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression )); gcmkONERROR(gckHARDWARE_SetPowerManagement( device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement )); #if gcdENABLE_FSCALE_VAL_ADJUST gcmkONERROR(gckHARDWARE_SetMinFscaleValue( device->kernels[gcvCORE_MAJOR]->hardware, Args->gpu3DMinClock )); #endif gcmkONERROR(gckHARDWARE_SetGpuProfiler( device->kernels[gcvCORE_MAJOR]->hardware, GpuProfiler )); } else { device->kernels[gcvCORE_MAJOR] = gcvNULL; } if (device->irqLines[gcvCORE_2D] != -1) { 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_SetPowerManagement( device->kernels[gcvCORE_2D]->hardware, 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) { #if gcdENABLE_VG gcmkONERROR(gckDEVICE_AddCore(device->device, gcvCORE_VG, gcvCHIP_ID_DEFAULT, device, &device->kernels[gcvCORE_VG])); gcmkONERROR(gckVGHARDWARE_SetPowerManagement( device->kernels[gcvCORE_VG]->vg->hardware, PowerManagement )); #endif } else { device->kernels[gcvCORE_VG] = gcvNULL; } /* Add core for multiple core. */ for (i = gcvCORE_3D1; i <= gcvCORE_3D_MAX; i++) { if (Args->irqs[i] != -1) { gcmkONERROR(gcTA_Construct(device->taos, (gceCORE)i, &globalTA[i])); gckDEVICE_AddCore(device->device, i, Args->chipIDs[i], device, &device->kernels[i]); gcmkONERROR( gckHARDWARE_SetFastClear(device->kernels[i]->hardware, FastClear, Compression)); gcmkONERROR(gckHARDWARE_SetPowerManagement( device->kernels[i]->hardware, PowerManagement )); gcmkONERROR(gckHARDWARE_SetGpuProfiler( device->kernels[i]->hardware, GpuProfiler )); } } /* Initialize the kernel thread semaphores. */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0); } device->signal = Signal; for (i = 0; i < gcdMAX_GPU_COUNT; i++) { if (device->kernels[i] != gcvNULL) break; } if (i == gcdMAX_GPU_COUNT) { gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); } #if gcdENABLE_VG if (i == gcvCORE_VG) { /* Query the ceiling of the system memory. */ gcmkONERROR(gckVGHARDWARE_QuerySystemMemory( device->kernels[i]->vg->hardware, &device->systemMemorySize, &device->systemMemoryBaseAddress )); } else #endif { /* Query the ceiling of the system memory. */ gcmkONERROR(gckHARDWARE_QuerySystemMemory( device->kernels[i]->hardware, &device->systemMemorySize, &device->systemMemoryBaseAddress )); } /* Grab the first availiable kernel */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { if (device->irqLines[i] != -1) { kernel = device->kernels[i]; break; } } /* Set up the internal memory region. */ if (device->internalSize > 0) { status = gckVIDMEM_Construct( device->os, internalBaseAddress, device->internalSize, internalAlignment, 0, &device->internalVidMem ); if (gcmIS_ERROR(status)) { /* Error, disable internal heap. */ device->internalSize = 0; } else { /* Map internal memory. */ device->internalLogical = (gctPOINTER) ioremap_nocache(physical, device->internalSize); if (device->internalLogical == gcvNULL) { gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } device->internalPhysical = (gctPHYS_ADDR)(gctUINTPTR_T) physical; physical += device->internalSize; } } if (device->externalSize > 0) { /* create the external memory heap */ status = gckVIDMEM_Construct( device->os, device->externalBase, device->externalSize, externalAlignment, 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; } } if (device->internalPhysical) { device->internalPhysicalName = gcmPTR_TO_NAME(device->internalPhysical); } if (device->externalPhysical) { device->externalPhysicalName = gcmPTR_TO_NAME(device->externalPhysical); } if (device->contiguousPhysical) { device->contiguousPhysicalName = gcmPTR_TO_NAME(device->contiguousPhysical); } /* Return pointer to the device. */ *Device = galDevice = device; gcmkONERROR(_DebugfsInit(device)); if (gckDEBUGFS_CreateNode( device, LogFileSize, device->debugfsDir.root ,DEBUG_FILE, &(device->dbgNode))) { gcmkTRACE_ZONE( gcvLEVEL_ERROR, gcvZONE_DRIVER, "%s(%d): Failed to create the debug file system %s/%s \n", __FUNCTION__, __LINE__, PARENT_FILE, DEBUG_FILE ); } else if (LogFileSize) { gckDEBUGFS_SetCurrentNode(device->dbgNode); } gcmkFOOTER_ARG("*Device=0x%x", * Device); return gcvSTATUS_OK; OnError: /* 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; gckKERNEL kernel = gcvNULL; gcmkHEADER_ARG("Device=0x%x", Device); if (Device != gcvNULL) { /* Grab the first availiable kernel */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { if (Device->irqLines[i] != -1) { kernel = Device->kernels[i]; break; } } if (Device->internalPhysicalName != 0) { gcmRELEASE_NAME(Device->internalPhysicalName); Device->internalPhysicalName = 0; } if (Device->externalPhysicalName != 0) { gcmRELEASE_NAME(Device->externalPhysicalName); Device->externalPhysicalName = 0; } if (Device->contiguousPhysicalName != 0) { gcmRELEASE_NAME(Device->contiguousPhysicalName); Device->contiguousPhysicalName = 0; } 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; } if (Device->externalPhysical != gcvNULL) { gckOS_ReleaseReservedMemory( Device->os, Device->externalPhysical ); } if (Device->externalLogical != gcvNULL) { Device->externalLogical = gcvNULL; } if (Device->externalVidMem != gcvNULL) { /* destroy the external heap */ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem)); Device->externalVidMem = gcvNULL; } if (Device->contiguousPhysical != gcvNULL) { if (Device->requestedContiguousBase == 0) { gcmkVERIFY_OK(_FreeMemory( Device, Device->contiguousLogical, Device->contiguousPhysical )); } else { gckOS_ReleaseReservedMemory( Device->os, Device->contiguousPhysical ); 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) { #if USE_LINUX_PCIE pci_iounmap(Device->platform->device, Device->registerBases[i]); #else iounmap(Device->registerBases[i]); release_mem_region(Device->requestedRegisterMemBases[i], Device->requestedRegisterMemSizes[i]); #endif } Device->registerBases[i] = gcvNULL; Device->requestedRegisterMemBases[i] = 0; Device->requestedRegisterMemSizes[i] = 0; } } 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; } 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; } if (Device->dbgNode) { gckDEBUGFS_FreeNode(Device->dbgNode); if(Device->dbgNode != gcvNULL) { kfree(Device->dbgNode); Device->dbgNode = gcvNULL; } } _DebugfsCleanup(Device); /* Free the device. */ kfree(Device); } gcmkFOOTER_NO(); return gcvSTATUS_OK; } 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 }; /******************************************************************************* ** ** gckGALDEVICE_Setup_ISR ** ** Start the ISR routine. ** ** INPUT: ** ** gckGALDEVICE Device ** Pointer to an gckGALDEVICE object. ** ** OUTPUT: ** ** Nothing. ** ** RETURNS: ** ** gcvSTATUS_OK ** Setup successfully. ** gcvSTATUS_GENERIC_IO ** Setup failed. */ gceSTATUS gckGALDEVICE_Setup_ISR( IN gceCORE Core ) { gceSTATUS status; gctINT ret = 0; gckGALDEVICE Device = galDevice; gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core); gcmkVERIFY_ARGUMENT(Device != NULL); if (Device->irqLines[Core] < 0) { gcmkONERROR(gcvSTATUS_GENERIC_IO); } #if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4)) { _Static_assert(gcvCORE_COUNT == gcmCOUNTOF(isrNames), "Core count is lager than isrNames size"); } #endif /* Hook up the isr based on the irq line. */ ret = request_irq( Device->irqLines[Core], isrRoutine, 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; gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return status; } gceSTATUS gckGALDEVICE_Setup_ISR_VG( IN gckGALDEVICE Device ) { gceSTATUS status; gctINT ret; gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); if (Device->irqLines[gcvCORE_VG] < 0) { gcmkONERROR(gcvSTATUS_GENERIC_IO); } /* Hook up the isr based on the irq line. */ ret = request_irq( Device->irqLines[gcvCORE_VG], isrRoutineVG, gcdIRQF_FLAG, isrNames[gcvCORE_VG], Device ); if (ret != 0) { gcmkTRACE_ZONE( gcvLEVEL_ERROR, gcvZONE_DRIVER, "%s(%d): Could not register irq line %d (error=%d)\n", __FUNCTION__, __LINE__, Device->irqLines[gcvCORE_VG], ret ); gcmkONERROR(gcvSTATUS_GENERIC_IO); } /* Mark ISR as initialized. */ Device->isrInitializeds[gcvCORE_VG] = gcvTRUE; gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return status; } /******************************************************************************* ** ** gckGALDEVICE_Release_ISR ** ** Release the irq line. ** ** INPUT: ** ** gckGALDEVICE Device ** Pointer to an gckGALDEVICE object. ** ** OUTPUT: ** ** Nothing. ** ** RETURNS: ** ** Nothing. */ gceSTATUS gckGALDEVICE_Release_ISR( IN gceCORE Core ) { gckGALDEVICE Device = galDevice; gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); /* release the irq */ if (Device->isrInitializeds[Core]) { free_irq(Device->irqLines[Core], (void *)(uintptr_t)(Core + 1)); Device->isrInitializeds[Core] = gcvFALSE; } gcmkFOOTER_NO(); return gcvSTATUS_OK; } gceSTATUS gckGALDEVICE_Release_ISR_VG( IN gckGALDEVICE Device ) { gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); /* release the irq */ if (Device->isrInitializeds[gcvCORE_VG]) { free_irq(Device->irqLines[gcvCORE_VG], Device); Device->isrInitializeds[gcvCORE_VG] = gcvFALSE; } gcmkFOOTER_NO(); return gcvSTATUS_OK; } /******************************************************************************* ** ** gckGALDEVICE_Start_Threads ** ** Start the daemon threads. ** ** INPUT: ** ** gckGALDEVICE Device ** Pointer to an gckGALDEVICE object. ** ** OUTPUT: ** ** Nothing. ** ** RETURNS: ** ** gcvSTATUS_OK ** Start successfully. ** gcvSTATUS_GENERIC_IO ** Start failed. */ gceSTATUS gckGALDEVICE_Start_Threads( IN gckGALDEVICE Device ) { gceSTATUS status; gctUINT i; gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); gcmkONERROR(_StartThread(threadRoutine, gcvCORE_MAJOR)); gcmkONERROR(_StartThread(threadRoutine, gcvCORE_2D)); gcmkONERROR(_StartThread(threadRoutine, gcvCORE_VG)); for (i = gcvCORE_3D1; i <= gcvCORE_3D_MAX; i++) { gcmkONERROR(_StartThread(threadRoutine, i)); } gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return status; } /******************************************************************************* ** ** gckGALDEVICE_Stop_Threads ** ** 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_Threads( gckGALDEVICE Device ) { gctINT i; gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); for (i = 0; i < gcdMAX_GPU_COUNT; i++) { /* Stop the kernel threads. */ if (Device->threadInitializeds[i]) { Device->killThread = gcvTRUE; up(&Device->semas[i]); kthread_stop(Device->threadCtxts[i]); Device->threadCtxts[i] = gcvNULL; Device->threadInitializeds[i] = gcvFALSE; } } 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 ) { gceSTATUS status; gctUINT i; gcmkHEADER_ARG("Device=0x%x", Device); /* Start the kernel thread. */ gcmkONERROR(gckGALDEVICE_Start_Threads(Device)); for (i = 0; i < gcvCORE_COUNT; i++) { if (i == gcvCORE_VG) { continue; } if (Device->kernels[i] != gcvNULL) { /* Setup the ISR routine. */ gcmkONERROR(gckGALDEVICE_Setup_ISR(i)); /* Switch to SUSPEND power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( Device->kernels[i]->hardware, gcvPOWER_OFF_BROADCAST )); } } if (Device->kernels[gcvCORE_VG] != gcvNULL) { /* Setup the ISR routine. */ gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device)); #if gcdENABLE_VG /* Switch to SUSPEND power state. */ gcmkONERROR(gckVGHARDWARE_SetPowerManagementState( Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF_BROADCAST )); #endif } gcmkFOOTER_NO(); return gcvSTATUS_OK; 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 ) { gceSTATUS status; gctUINT i; gcmkHEADER_ARG("Device=0x%x", Device); gcmkVERIFY_ARGUMENT(Device != NULL); for (i = 0; i < gcvCORE_COUNT; i++) { if (i == gcvCORE_VG) { continue; } if (Device->kernels[i] != gcvNULL) { gcmkONERROR(gckHARDWARE_SetPowerManagement( Device->kernels[i]->hardware, gcvTRUE )); /* Switch to OFF power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( Device->kernels[i]->hardware, gcvPOWER_OFF )); /* Remove the ISR routine. */ gcmkONERROR(gckGALDEVICE_Release_ISR(i)); } } if (Device->kernels[gcvCORE_VG] != gcvNULL) { /* Setup the ISR routine. */ gcmkONERROR(gckGALDEVICE_Release_ISR_VG(Device)); #if gcdENABLE_VG /* Switch to OFF power state. */ gcmkONERROR(gckVGHARDWARE_SetPowerManagementState( Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF )); #endif } /* Stop the kernel thread. */ gcmkONERROR(gckGALDEVICE_Stop_Threads(Device)); gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return status; } /******************************************************************************* ** ** gckGALDEVICE_AddCore ** ** Add a core after gckGALDevice is constructed. ** ** INPUT: ** ** OUTPUT: ** */ gceSTATUS gckGALDEVICE_AddCore( IN gckGALDEVICE Device, IN gcsDEVICE_CONSTRUCT_ARGS * Args ) { gceSTATUS status; gceCORE core = gcvCORE_COUNT; gctUINT i = 0; gcmkHEADER(); gcmkVERIFY_ARGUMENT(Device != gcvNULL); /* Find which core is added. */ for (i = 0; i < gcvCORE_COUNT; i++) { if (Args->irqs[i] != -1) { core = i; break; } } if (i == gcvCORE_COUNT) { gcmkPRINT("[galcore]: No valid core information found"); gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); } gcmkPRINT("[galcore]: add core[%d]", core); /* Record irq, registerBase, registerSize. */ Device->irqLines[core] = Args->irqs[core]; _SetupRegisterPhysical(Device, Args); /* Map register memory.*/ /* Add a platform indepedent framework. */ gcmkONERROR(gckDEVICE_AddCore( Device->device, core, Args->chipIDs[core], Device, &Device->kernels[core] )); /* Start thread routine. */ _StartThread(threadRoutine, core); /* Register ISR. */ gckGALDEVICE_Setup_ISR(core); /* Set default power management state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( Device->kernels[core]->hardware, gcvPOWER_OFF_BROADCAST )); gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: gcmkFOOTER(); return gcvSTATUS_OK; }