blob: 28e2b00584036bd989fd33a26dd5fc9344e8a8fb [file] [log] [blame]
/**********************************************************************
*
* Copyright (C) Imagination Technologies Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful but, except
* as otherwise stated in writing, 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Contact Information:
* Imagination Technologies Ltd. <gpl-support@imgtec.com>
* Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
*
******************************************************************************/
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38))
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#endif
#include <asm/io.h>
#include <asm/page.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22))
#include <asm/system.h>
#endif
#include <asm/cacheflush.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/hugetlb.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/hardirq.h>
#include <linux/timer.h>
#include <linux/capability.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) || \
defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) || \
defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || \
defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) || \
defined(PVR_LINUX_USING_WORKQUEUES)
#include <linux/workqueue.h>
#endif
#include "img_types.h"
#include "services_headers.h"
#include "mm.h"
#include "pvrmmap.h"
#include "mmap.h"
#include "env_data.h"
#include "proc.h"
#include "mutex.h"
#include "event.h"
#include "linkage.h"
#include "pvr_uaccess.h"
#include "lock.h"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
#define ON_EACH_CPU(func, info, wait) on_each_cpu(func, info, wait)
#else
#define ON_EACH_CPU(func, info, wait) on_each_cpu(func, info, 0, wait)
#endif
#if defined(PVR_LINUX_USING_WORKQUEUES) && !defined(CONFIG_PREEMPT)
#error "A preemptible Linux kernel is required when using workqueues"
#endif
#if defined(EMULATOR)
#define EVENT_OBJECT_TIMEOUT_MS (2000)
#else
#define EVENT_OBJECT_TIMEOUT_MS (100)
#endif
#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID *ppvCpuVAddr, IMG_HANDLE *phBlockAlloc)
#else
PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID *ppvCpuVAddr, IMG_HANDLE *phBlockAlloc, IMG_CHAR *pszFilename, IMG_UINT32 ui32Line)
#endif
{
PVR_UNREFERENCED_PARAMETER(ui32Flags);
PVR_UNREFERENCED_PARAMETER(phBlockAlloc);
if (ui32Size > PAGE_SIZE)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
*ppvCpuVAddr = _VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED, pszFilename, ui32Line);
#else
*ppvCpuVAddr = VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED);
#endif
if (*ppvCpuVAddr)
{
return PVRSRV_OK;
}
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
*ppvCpuVAddr = _KMallocWrapper(ui32Size, GFP_KERNEL | __GFP_NOWARN, pszFilename, ui32Line);
#else
*ppvCpuVAddr = KMallocWrapper(ui32Size, GFP_KERNEL | __GFP_NOWARN);
#endif
if (!*ppvCpuVAddr)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
return PVRSRV_OK;
}
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24))
static inline int is_vmalloc_addr(const void *pvCpuVAddr)
{
unsigned long lAddr = (unsigned long)pvCpuVAddr;
return lAddr >= VMALLOC_START && lAddr < VMALLOC_END;
}
#endif
#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
PVRSRV_ERROR OSFreeMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID pvCpuVAddr, IMG_HANDLE hBlockAlloc)
#else
PVRSRV_ERROR OSFreeMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID pvCpuVAddr, IMG_HANDLE hBlockAlloc, IMG_CHAR *pszFilename, IMG_UINT32 ui32Line)
#endif
{
PVR_UNREFERENCED_PARAMETER(ui32Flags);
PVR_UNREFERENCED_PARAMETER(ui32Size);
PVR_UNREFERENCED_PARAMETER(hBlockAlloc);
if (is_vmalloc_addr(pvCpuVAddr))
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
_VFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
#else
VFreeWrapper(pvCpuVAddr);
#endif
}
else
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
_KFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
#else
KFreeWrapper(pvCpuVAddr);
#endif
}
return PVRSRV_OK;
}
PVRSRV_ERROR
OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags,
IMG_UINT32 ui32Size,
IMG_UINT32 ui32PageSize,
IMG_PVOID pvPrivData,
IMG_UINT32 ui32PrivDataLength,
IMG_VOID **ppvCpuVAddr,
IMG_HANDLE *phOSMemHandle)
{
LinuxMemArea *psLinuxMemArea;
PVR_UNREFERENCED_PARAMETER(ui32PageSize);
#if 0
if(ui32AllocFlags & PVRSRV_HAP_SINGLE_PROCESS)
{
ui32AllocFlags &= ~PVRSRV_HAP_SINGLE_PROCESS;
ui32AllocFlags |= PVRSRV_HAP_MULTI_PROCESS;
}
#endif
if(ui32AllocFlags & PVRSRV_MEM_ION)
{
BUG_ON((ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) != PVRSRV_HAP_SINGLE_PROCESS);
psLinuxMemArea = NewIONLinuxMemArea(ui32Size, ui32AllocFlags,
pvPrivData, ui32PrivDataLength);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
PVRMMapRegisterArea(psLinuxMemArea);
goto ExitSkipSwitch;
}
switch(ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
{
psLinuxMemArea = NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
break;
}
case PVRSRV_HAP_SINGLE_PROCESS:
{
psLinuxMemArea = NewAllocPagesLinuxMemArea(ui32Size, ui32AllocFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
case PVRSRV_HAP_MULTI_PROCESS:
{
#if defined(VIVT_CACHE) || defined(__sh__)
ui32AllocFlags &= ~PVRSRV_HAP_CACHED;
#endif
psLinuxMemArea = NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
default:
PVR_DPF((PVR_DBG_ERROR, "OSAllocPages: invalid flags 0x%x\n", ui32AllocFlags));
*ppvCpuVAddr = NULL;
*phOSMemHandle = (IMG_HANDLE)0;
return PVRSRV_ERROR_INVALID_PARAMS;
}
ExitSkipSwitch:
*ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
*phOSMemHandle = psLinuxMemArea;
LinuxMemAreaRegister(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSFreePages(IMG_UINT32 ui32AllocFlags, IMG_UINT32 ui32Bytes, IMG_VOID *pvCpuVAddr, IMG_HANDLE hOSMemHandle)
{
LinuxMemArea *psLinuxMemArea;
PVRSRV_ERROR eError;
PVR_UNREFERENCED_PARAMETER(ui32Bytes);
PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
switch(ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
break;
case PVRSRV_HAP_SINGLE_PROCESS:
case PVRSRV_HAP_MULTI_PROCESS:
eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"OSFreePages(ui32AllocFlags=0x%08X, ui32Bytes=%d, "
"pvCpuVAddr=%p, hOSMemHandle=%p) FAILED!",
ui32AllocFlags, ui32Bytes, pvCpuVAddr, hOSMemHandle));
return eError;
}
break;
default:
PVR_DPF((PVR_DBG_ERROR,"%s: invalid flags 0x%x\n",
__FUNCTION__, ui32AllocFlags));
return PVRSRV_ERROR_INVALID_PARAMS;
}
LinuxMemAreaDeepFree(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSGetSubMemHandle(IMG_HANDLE hOSMemHandle,
IMG_UINT32 ui32ByteOffset,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32Flags,
IMG_HANDLE *phOSMemHandleRet)
{
LinuxMemArea *psParentLinuxMemArea, *psLinuxMemArea;
PVRSRV_ERROR eError;
psParentLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
psLinuxMemArea = NewSubLinuxMemArea(psParentLinuxMemArea, ui32ByteOffset, ui32Bytes);
if(!psLinuxMemArea)
{
*phOSMemHandleRet = NULL;
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
*phOSMemHandleRet = psLinuxMemArea;
if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY)
{
return PVRSRV_OK;
}
eError = PVRMMapRegisterArea(psLinuxMemArea);
if(eError != PVRSRV_OK)
{
goto failed_register_area;
}
return PVRSRV_OK;
failed_register_area:
*phOSMemHandleRet = NULL;
LinuxMemAreaDeepFree(psLinuxMemArea);
return eError;
}
PVRSRV_ERROR
OSReleaseSubMemHandle(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32Flags)
{
LinuxMemArea *psLinuxMemArea;
PVRSRV_ERROR eError;
psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC);
if((ui32Flags & PVRSRV_HAP_KERNEL_ONLY) == 0)
{
eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
if(eError != PVRSRV_OK)
{
return eError;
}
}
LinuxMemAreaDeepFree(psLinuxMemArea);
return PVRSRV_OK;
}
IMG_CPU_PHYADDR
OSMemHandleToCpuPAddr(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32ByteOffset)
{
PVR_ASSERT(hOSMemHandle);
return LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset);
}
IMG_BOOL OSMemHandleIsPhysContig(IMG_VOID *hOSMemHandle)
{
LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
PVR_ASSERT(psLinuxMemArea);
if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_EXTERNAL_KV)
return psLinuxMemArea->uData.sExternalKV.bPhysContig;
return IMG_FALSE;
}
IMG_VOID OSMemCopy(IMG_VOID *pvDst, IMG_VOID *pvSrc, IMG_UINT32 ui32Size)
{
#if defined(USE_UNOPTIMISED_MEMCPY)
IMG_UINT8 *Src,*Dst;
IMG_INT i;
Src=(IMG_UINT8 *)pvSrc;
Dst=(IMG_UINT8 *)pvDst;
for(i=0;i<ui32Size;i++)
{
Dst[i]=Src[i];
}
#else
memcpy(pvDst, pvSrc, ui32Size);
#endif
}
IMG_VOID OSMemSet(IMG_VOID *pvDest, IMG_UINT8 ui8Value, IMG_UINT32 ui32Size)
{
#if defined(USE_UNOPTIMISED_MEMSET)
IMG_UINT8 *Buff;
IMG_INT i;
Buff=(IMG_UINT8 *)pvDest;
for(i=0;i<ui32Size;i++)
{
Buff[i]=ui8Value;
}
#else
memset(pvDest, (IMG_INT) ui8Value, (size_t) ui32Size);
#endif
}
IMG_CHAR *OSStringCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc)
{
return (strcpy(pszDest, pszSrc));
}
IMG_INT32 OSSNPrintf(IMG_CHAR *pStr, IMG_UINT32 ui32Size, const IMG_CHAR *pszFormat, ...)
{
va_list argList;
IMG_INT32 iCount;
va_start(argList, pszFormat);
iCount = vsnprintf(pStr, (size_t)ui32Size, pszFormat, argList);
va_end(argList);
return iCount;
}
IMG_VOID OSBreakResourceLock (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID)
{
volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock;
if(*pui32Access)
{
if(psResource->ui32ID == ui32ID)
{
psResource->ui32ID = 0;
*pui32Access = 0;
}
else
{
PVR_DPF((PVR_DBG_MESSAGE,"OSBreakResourceLock: Resource is not locked for this process."));
}
}
else
{
PVR_DPF((PVR_DBG_MESSAGE,"OSBreakResourceLock: Resource is not locked"));
}
}
PVRSRV_ERROR OSCreateResource(PVRSRV_RESOURCE *psResource)
{
psResource->ui32ID = 0;
psResource->ui32Lock = 0;
return PVRSRV_OK;
}
PVRSRV_ERROR OSDestroyResource (PVRSRV_RESOURCE *psResource)
{
OSBreakResourceLock (psResource, psResource->ui32ID);
return PVRSRV_OK;
}
PVRSRV_ERROR OSInitEnvData(IMG_PVOID *ppvEnvSpecificData)
{
ENV_DATA *psEnvData;
PVRSRV_ERROR eError;
eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), (IMG_VOID **)&psEnvData, IMG_NULL,
"Environment Data");
if (eError != PVRSRV_OK)
{
return eError;
}
eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE,
&psEnvData->pvBridgeData, IMG_NULL,
"Bridge Data");
if (eError != PVRSRV_OK)
{
OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), psEnvData, IMG_NULL);
return eError;
}
psEnvData->bMISRInstalled = IMG_FALSE;
psEnvData->bLISRInstalled = IMG_FALSE;
*ppvEnvSpecificData = psEnvData;
return PVRSRV_OK;
}
PVRSRV_ERROR OSDeInitEnvData(IMG_PVOID pvEnvSpecificData)
{
ENV_DATA *psEnvData = (ENV_DATA*)pvEnvSpecificData;
PVR_ASSERT(!psEnvData->bMISRInstalled);
PVR_ASSERT(!psEnvData->bLISRInstalled);
OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE, psEnvData->pvBridgeData, IMG_NULL);
psEnvData->pvBridgeData = IMG_NULL;
OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), pvEnvSpecificData, IMG_NULL);
return PVRSRV_OK;
}
IMG_VOID OSReleaseThreadQuanta(IMG_VOID)
{
schedule();
}
IMG_UINT32 OSClockus(IMG_VOID)
{
IMG_UINT32 time, j = jiffies;
time = j * (1000000 / HZ);
return time;
}
IMG_VOID OSWaitus(IMG_UINT32 ui32Timeus)
{
udelay(ui32Timeus);
}
IMG_VOID OSSleepms(IMG_UINT32 ui32Timems)
{
msleep(ui32Timems);
}
IMG_HANDLE OSFuncHighResTimerCreate(IMG_VOID)
{
return (IMG_HANDLE) 1;
}
IMG_UINT32 OSFuncHighResTimerGetus(IMG_HANDLE hTimer)
{
return (IMG_UINT32) jiffies_to_usecs(jiffies);
}
IMG_VOID OSFuncHighResTimerDestroy(IMG_HANDLE hTimer)
{
PVR_UNREFERENCED_PARAMETER(hTimer);
}
IMG_UINT32 OSGetCurrentProcessIDKM(IMG_VOID)
{
if (in_interrupt())
{
return KERNEL_ID;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
return (IMG_UINT32)current->pgrp;
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
return (IMG_UINT32)task_tgid_nr(current);
#else
return (IMG_UINT32)current->tgid;
#endif
#endif
}
IMG_UINT32 OSGetPageSize(IMG_VOID)
{
#if defined(__sh__)
IMG_UINT32 ui32ReturnValue = PAGE_SIZE;
return (ui32ReturnValue);
#else
return PAGE_SIZE;
#endif
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
static irqreturn_t DeviceISRWrapper(int irq, void *dev_id
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
, struct pt_regs *regs
#endif
)
{
PVRSRV_DEVICE_NODE *psDeviceNode;
IMG_BOOL bStatus = IMG_FALSE;
PVR_UNREFERENCED_PARAMETER(irq);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
PVR_UNREFERENCED_PARAMETER(regs);
#endif
psDeviceNode = (PVRSRV_DEVICE_NODE*)dev_id;
if(!psDeviceNode)
{
PVR_DPF((PVR_DBG_ERROR, "DeviceISRWrapper: invalid params\n"));
goto out;
}
bStatus = PVRSRVDeviceLISR(psDeviceNode);
if (bStatus)
{
OSScheduleMISR((IMG_VOID *)psDeviceNode->psSysData);
}
out:
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
return bStatus ? IRQ_HANDLED : IRQ_NONE;
#endif
}
static irqreturn_t SystemISRWrapper(int irq, void *dev_id
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
, struct pt_regs *regs
#endif
)
{
SYS_DATA *psSysData;
IMG_BOOL bStatus = IMG_FALSE;
PVR_UNREFERENCED_PARAMETER(irq);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
PVR_UNREFERENCED_PARAMETER(regs);
#endif
psSysData = (SYS_DATA *)dev_id;
if(!psSysData)
{
PVR_DPF((PVR_DBG_ERROR, "SystemISRWrapper: invalid params\n"));
goto out;
}
bStatus = PVRSRVSystemLISR(psSysData);
if (bStatus)
{
OSScheduleMISR((IMG_VOID *)psSysData);
}
out:
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
return bStatus ? IRQ_HANDLED : IRQ_NONE;
#endif
}
PVRSRV_ERROR OSInstallDeviceLISR(IMG_VOID *pvSysData,
IMG_UINT32 ui32Irq,
IMG_CHAR *pszISRName,
IMG_VOID *pvDeviceNode)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (psEnvData->bLISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallDeviceLISR: An ISR has already been installed: IRQ %d cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie));
return PVRSRV_ERROR_ISR_ALREADY_INSTALLED;
}
PVR_TRACE(("Installing device LISR %s on IRQ %d with cookie %p", pszISRName, ui32Irq, pvDeviceNode));
if(request_irq(ui32Irq, DeviceISRWrapper,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
SA_SHIRQ
#else
IRQF_SHARED
#endif
, pszISRName, pvDeviceNode))
{
PVR_DPF((PVR_DBG_ERROR,"OSInstallDeviceLISR: Couldn't install device LISR on IRQ %d", ui32Irq));
return PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR;
}
psEnvData->ui32IRQ = ui32Irq;
psEnvData->pvISRCookie = pvDeviceNode;
psEnvData->bLISRInstalled = IMG_TRUE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSUninstallDeviceLISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (!psEnvData->bLISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSUninstallDeviceLISR: No LISR has been installed"));
return PVRSRV_ERROR_ISR_NOT_INSTALLED;
}
PVR_TRACE(("Uninstalling device LISR on IRQ %d with cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie));
free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie);
psEnvData->bLISRInstalled = IMG_FALSE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSInstallSystemLISR(IMG_VOID *pvSysData, IMG_UINT32 ui32Irq)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (psEnvData->bLISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallSystemLISR: An LISR has already been installed: IRQ %d cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie));
return PVRSRV_ERROR_ISR_ALREADY_INSTALLED;
}
PVR_TRACE(("Installing system LISR on IRQ %d with cookie %p", ui32Irq, pvSysData));
if(request_irq(ui32Irq, SystemISRWrapper,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
SA_SHIRQ
#else
IRQF_SHARED
#endif
, PVRSRV_MODNAME, pvSysData))
{
PVR_DPF((PVR_DBG_ERROR,"OSInstallSystemLISR: Couldn't install system LISR on IRQ %d", ui32Irq));
return PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR;
}
psEnvData->ui32IRQ = ui32Irq;
psEnvData->pvISRCookie = pvSysData;
psEnvData->bLISRInstalled = IMG_TRUE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSUninstallSystemLISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (!psEnvData->bLISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSUninstallSystemLISR: No LISR has been installed"));
return PVRSRV_ERROR_ISR_NOT_INSTALLED;
}
PVR_TRACE(("Uninstalling system LISR on IRQ %d with cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie));
free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie);
psEnvData->bLISRInstalled = IMG_FALSE;
return PVRSRV_OK;
}
#if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE)
static void MISRWrapper(
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
void *data
#else
struct work_struct *data
#endif
)
{
ENV_DATA *psEnvData = container_of(data, ENV_DATA, sMISRWork);
SYS_DATA *psSysData = (SYS_DATA *)psEnvData->pvMISRData;
PVRSRVMISR(psSysData);
}
PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed"));
return PVRSRV_ERROR_ISR_ALREADY_INSTALLED;
}
PVR_TRACE(("Installing MISR with cookie %p", pvSysData));
psEnvData->psWorkQueue = create_singlethread_workqueue("pvr_workqueue");
if (psEnvData->psWorkQueue == IMG_NULL)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: create_singlethreaded_workqueue failed"));
return PVRSRV_ERROR_UNABLE_TO_CREATE_THREAD;
}
INIT_WORK(&psEnvData->sMISRWork, MISRWrapper
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
, (void *)&psEnvData->sMISRWork
#endif
);
psEnvData->pvMISRData = pvSysData;
psEnvData->bMISRInstalled = IMG_TRUE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (!psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed"));
return PVRSRV_ERROR_ISR_NOT_INSTALLED;
}
PVR_TRACE(("Uninstalling MISR"));
destroy_workqueue(psEnvData->psWorkQueue);
psEnvData->bMISRInstalled = IMG_FALSE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
queue_work(psEnvData->psWorkQueue, &psEnvData->sMISRWork);
}
return PVRSRV_OK;
}
#else
#if defined(PVR_LINUX_MISR_USING_WORKQUEUE)
static void MISRWrapper(
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
void *data
#else
struct work_struct *data
#endif
)
{
ENV_DATA *psEnvData = container_of(data, ENV_DATA, sMISRWork);
SYS_DATA *psSysData = (SYS_DATA *)psEnvData->pvMISRData;
PVRSRVMISR(psSysData);
}
PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed"));
return PVRSRV_ERROR_ISR_ALREADY_INSTALLED;
}
PVR_TRACE(("Installing MISR with cookie %p", pvSysData));
INIT_WORK(&psEnvData->sMISRWork, MISRWrapper
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
, (void *)&psEnvData->sMISRWork
#endif
);
psEnvData->pvMISRData = pvSysData;
psEnvData->bMISRInstalled = IMG_TRUE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (!psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed"));
return PVRSRV_ERROR_ISR_NOT_INSTALLED;
}
PVR_TRACE(("Uninstalling MISR"));
flush_scheduled_work();
psEnvData->bMISRInstalled = IMG_FALSE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
schedule_work(&psEnvData->sMISRWork);
}
return PVRSRV_OK;
}
#else
static void MISRWrapper(unsigned long data)
{
SYS_DATA *psSysData;
psSysData = (SYS_DATA *)data;
PVRSRVMISR(psSysData);
}
PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed"));
return PVRSRV_ERROR_ISR_ALREADY_INSTALLED;
}
PVR_TRACE(("Installing MISR with cookie %p", pvSysData));
tasklet_init(&psEnvData->sMISRTasklet, MISRWrapper, (unsigned long)pvSysData);
psEnvData->bMISRInstalled = IMG_TRUE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData;
if (!psEnvData->bMISRInstalled)
{
PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed"));
return PVRSRV_ERROR_ISR_NOT_INSTALLED;
}
PVR_TRACE(("Uninstalling MISR"));
tasklet_kill(&psEnvData->sMISRTasklet);
psEnvData->bMISRInstalled = IMG_FALSE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData)
{
SYS_DATA *psSysData = (SYS_DATA*)pvSysData;
ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData;
if (psEnvData->bMISRInstalled)
{
tasklet_schedule(&psEnvData->sMISRTasklet);
}
return PVRSRV_OK;
}
#endif
#endif
#endif
IMG_VOID OSPanic(IMG_VOID)
{
BUG();
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22))
#define OS_TAS(p) xchg((p), 1)
#else
#define OS_TAS(p) tas(p)
#endif
PVRSRV_ERROR OSLockResource ( PVRSRV_RESOURCE *psResource,
IMG_UINT32 ui32ID)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(!OS_TAS(&psResource->ui32Lock))
psResource->ui32ID = ui32ID;
else
eError = PVRSRV_ERROR_UNABLE_TO_LOCK_RESOURCE;
return eError;
}
PVRSRV_ERROR OSUnlockResource (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID)
{
volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock;
PVRSRV_ERROR eError = PVRSRV_OK;
if(*pui32Access)
{
if(psResource->ui32ID == ui32ID)
{
psResource->ui32ID = 0;
smp_mb();
*pui32Access = 0;
}
else
{
PVR_DPF((PVR_DBG_ERROR,"OSUnlockResource: Resource %p is not locked with expected value.", psResource));
PVR_DPF((PVR_DBG_MESSAGE,"Should be %x is actually %x", ui32ID, psResource->ui32ID));
eError = PVRSRV_ERROR_INVALID_LOCK_ID;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR,"OSUnlockResource: Resource %p is not locked", psResource));
eError = PVRSRV_ERROR_RESOURCE_NOT_LOCKED;
}
return eError;
}
IMG_BOOL OSIsResourceLocked (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID)
{
volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock;
return (*(volatile IMG_UINT32 *)pui32Access == 1) && (psResource->ui32ID == ui32ID)
? IMG_TRUE
: IMG_FALSE;
}
#if !defined(SYS_CUSTOM_POWERLOCK_WRAP)
PVRSRV_ERROR OSPowerLockWrap(IMG_BOOL bTryLock)
{
PVR_UNREFERENCED_PARAMETER(bTryLock);
return PVRSRV_OK;
}
IMG_VOID OSPowerLockUnwrap (IMG_VOID)
{
}
#endif
IMG_CPU_PHYADDR OSMapLinToCPUPhys(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvLinAddr)
{
IMG_CPU_PHYADDR CpuPAddr;
LinuxMemArea *psLinuxMemArea;
IMG_UINTPTR_T uiByteOffset;
IMG_UINT32 ui32ByteOffset;
PVR_ASSERT(hOSMemHandle != IMG_NULL);
psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
uiByteOffset = (IMG_UINTPTR_T)pvLinAddr - (IMG_UINTPTR_T)LinuxMemAreaToCpuVAddr(psLinuxMemArea);
ui32ByteOffset = (IMG_UINT32)uiByteOffset;
CpuPAddr = LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset);
return CpuPAddr;
}
IMG_VOID *
OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_HANDLE *phOSMemHandle)
{
if(ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY)
{
if(phOSMemHandle == IMG_NULL)
{
IMG_VOID *pvIORemapCookie;
pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags);
if(pvIORemapCookie == IMG_NULL)
{
return IMG_NULL;
}
return pvIORemapCookie;
}
else
{
LinuxMemArea *psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags);
if(psLinuxMemArea == IMG_NULL)
{
return IMG_NULL;
}
*phOSMemHandle = (IMG_HANDLE)psLinuxMemArea;
return LinuxMemAreaToCpuVAddr(psLinuxMemArea);
}
}
PVR_DPF((PVR_DBG_ERROR,
"OSMapPhysToLin should only be used with PVRSRV_HAP_KERNEL_ONLY "
" (Use OSReservePhys otherwise)"));
return IMG_NULL;
}
IMG_BOOL
OSUnMapPhysToLin(IMG_VOID *pvLinAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32MappingFlags, IMG_HANDLE hOSMemHandle)
{
PVR_TRACE(("%s: unmapping %d bytes from %p", __FUNCTION__, ui32Bytes, pvLinAddr));
PVR_UNREFERENCED_PARAMETER(ui32Bytes);
if(ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY)
{
if (hOSMemHandle == IMG_NULL)
{
IOUnmapWrapper(pvLinAddr);
}
else
{
LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
PVR_ASSERT(LinuxMemAreaToCpuVAddr(psLinuxMemArea) == pvLinAddr);
FreeIORemapLinuxMemArea(psLinuxMemArea);
}
return IMG_TRUE;
}
PVR_DPF((PVR_DBG_ERROR,
"OSUnMapPhysToLin should only be used with PVRSRV_HAP_KERNEL_ONLY "
" (Use OSUnReservePhys otherwise)"));
return IMG_FALSE;
}
static PVRSRV_ERROR
RegisterExternalMem(IMG_SYS_PHYADDR *pBasePAddr,
IMG_VOID *pvCPUVAddr,
IMG_UINT32 ui32Bytes,
IMG_BOOL bPhysContig,
IMG_UINT32 ui32MappingFlags,
IMG_HANDLE *phOSMemHandle)
{
LinuxMemArea *psLinuxMemArea;
switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
{
psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
break;
}
case PVRSRV_HAP_SINGLE_PROCESS:
{
psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
case PVRSRV_HAP_MULTI_PROCESS:
{
#if defined(VIVT_CACHE) || defined(__sh__)
ui32MappingFlags &= ~PVRSRV_HAP_CACHED;
#endif
psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
default:
PVR_DPF((PVR_DBG_ERROR,"OSRegisterMem : invalid flags 0x%x\n", ui32MappingFlags));
*phOSMemHandle = (IMG_HANDLE)0;
return PVRSRV_ERROR_INVALID_FLAGS;
}
*phOSMemHandle = (IMG_HANDLE)psLinuxMemArea;
LinuxMemAreaRegister(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSRegisterMem(IMG_CPU_PHYADDR BasePAddr,
IMG_VOID *pvCPUVAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_HANDLE *phOSMemHandle)
{
IMG_SYS_PHYADDR SysPAddr = SysCpuPAddrToSysPAddr(BasePAddr);
return RegisterExternalMem(&SysPAddr, pvCPUVAddr, ui32Bytes, IMG_TRUE, ui32MappingFlags, phOSMemHandle);
}
PVRSRV_ERROR OSRegisterDiscontigMem(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32MappingFlags, IMG_HANDLE *phOSMemHandle)
{
return RegisterExternalMem(pBasePAddr, pvCPUVAddr, ui32Bytes, IMG_FALSE, ui32MappingFlags, phOSMemHandle);
}
PVRSRV_ERROR
OSUnRegisterMem (IMG_VOID *pvCpuVAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_HANDLE hOSMemHandle)
{
LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
PVRSRV_ERROR eError;
PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
PVR_UNREFERENCED_PARAMETER(ui32Bytes);
switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
break;
case PVRSRV_HAP_SINGLE_PROCESS:
case PVRSRV_HAP_MULTI_PROCESS:
{
eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s(%p, %d, 0x%08X, %p) FAILED!",
__FUNCTION__, pvCpuVAddr, ui32Bytes,
ui32MappingFlags, hOSMemHandle));
return eError;
}
break;
}
default:
{
PVR_DPF((PVR_DBG_ERROR, "OSUnRegisterMem : invalid flags 0x%x", ui32MappingFlags));
return PVRSRV_ERROR_INVALID_PARAMS;
}
}
LinuxMemAreaDeepFree(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR OSUnRegisterDiscontigMem(IMG_VOID *pvCpuVAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32Flags, IMG_HANDLE hOSMemHandle)
{
return OSUnRegisterMem(pvCpuVAddr, ui32Bytes, ui32Flags, hOSMemHandle);
}
PVRSRV_ERROR
OSReservePhys(IMG_CPU_PHYADDR BasePAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_VOID **ppvCpuVAddr,
IMG_HANDLE *phOSMemHandle)
{
LinuxMemArea *psLinuxMemArea;
#if 0
if(ui32MappingFlags & PVRSRV_HAP_SINGLE_PROCESS)
{
ui32MappingFlags &= ~PVRSRV_HAP_SINGLE_PROCESS;
ui32MappingFlags |= PVRSRV_HAP_MULTI_PROCESS;
}
#endif
switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
{
psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
break;
}
case PVRSRV_HAP_SINGLE_PROCESS:
{
psLinuxMemArea = NewIOLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
case PVRSRV_HAP_MULTI_PROCESS:
{
#if defined(VIVT_CACHE) || defined(__sh__)
ui32MappingFlags &= ~PVRSRV_HAP_CACHED;
#endif
psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags);
if(!psLinuxMemArea)
{
return PVRSRV_ERROR_BAD_MAPPING;
}
PVRMMapRegisterArea(psLinuxMemArea);
break;
}
default:
PVR_DPF((PVR_DBG_ERROR,"OSMapPhysToLin : invalid flags 0x%x\n", ui32MappingFlags));
*ppvCpuVAddr = NULL;
*phOSMemHandle = (IMG_HANDLE)0;
return PVRSRV_ERROR_INVALID_FLAGS;
}
*phOSMemHandle = (IMG_HANDLE)psLinuxMemArea;
*ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
LinuxMemAreaRegister(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSUnReservePhys(IMG_VOID *pvCpuVAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_HANDLE hOSMemHandle)
{
LinuxMemArea *psLinuxMemArea;
PVRSRV_ERROR eError;
PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
PVR_UNREFERENCED_PARAMETER(ui32Bytes);
psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK)
{
case PVRSRV_HAP_KERNEL_ONLY:
break;
case PVRSRV_HAP_SINGLE_PROCESS:
case PVRSRV_HAP_MULTI_PROCESS:
{
eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s(%p, %d, 0x%08X, %p) FAILED!",
__FUNCTION__, pvCpuVAddr, ui32Bytes,
ui32MappingFlags, hOSMemHandle));
return eError;
}
break;
}
default:
{
PVR_DPF((PVR_DBG_ERROR, "OSUnMapPhysToLin : invalid flags 0x%x", ui32MappingFlags));
return PVRSRV_ERROR_INVALID_PARAMS;
}
}
LinuxMemAreaDeepFree(psLinuxMemArea);
return PVRSRV_OK;
}
PVRSRV_ERROR OSBaseAllocContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR *pvLinAddr, IMG_CPU_PHYADDR *psPhysAddr)
{
#if !defined(NO_HARDWARE)
PVR_UNREFERENCED_PARAMETER(ui32Size);
PVR_UNREFERENCED_PARAMETER(pvLinAddr);
PVR_UNREFERENCED_PARAMETER(psPhysAddr);
PVR_DPF((PVR_DBG_ERROR, "%s: Not available", __FUNCTION__));
return PVRSRV_ERROR_OUT_OF_MEMORY;
#else
IMG_VOID *pvKernLinAddr;
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
pvKernLinAddr = _KMallocWrapper(ui32Size, GFP_KERNEL, __FILE__, __LINE__);
#else
pvKernLinAddr = KMallocWrapper(ui32Size, GFP_KERNEL);
#endif
if (!pvKernLinAddr)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
*pvLinAddr = pvKernLinAddr;
psPhysAddr->uiAddr = virt_to_phys(pvKernLinAddr);
return PVRSRV_OK;
#endif
}
PVRSRV_ERROR OSBaseFreeContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR pvLinAddr, IMG_CPU_PHYADDR psPhysAddr)
{
#if !defined(NO_HARDWARE)
PVR_UNREFERENCED_PARAMETER(ui32Size);
PVR_UNREFERENCED_PARAMETER(pvLinAddr);
PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr);
PVR_DPF((PVR_DBG_WARNING, "%s: Not available", __FUNCTION__));
#else
PVR_UNREFERENCED_PARAMETER(ui32Size);
PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr);
KFreeWrapper(pvLinAddr);
#endif
return PVRSRV_OK;
}
IMG_UINT32 OSReadHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset)
{
#if !defined(NO_HARDWARE)
return (IMG_UINT32) readl((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#else
return *(IMG_UINT32 *)((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#endif
}
IMG_VOID OSWriteHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT32 ui32Value)
{
#if !defined(NO_HARDWARE)
writel(ui32Value, (IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#else
*(IMG_UINT32 *)((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset) = ui32Value;
#endif
}
#if defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14))
PVRSRV_PCI_DEV_HANDLE OSPCISetDev(IMG_VOID *pvPCICookie, HOST_PCI_INIT_FLAGS eFlags)
{
int err;
IMG_UINT32 i;
PVR_PCI_DEV *psPVRPCI;
PVR_TRACE(("OSPCISetDev"));
if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psPVRPCI), (IMG_VOID **)&psPVRPCI, IMG_NULL,
"PCI Device") != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCISetDev: Couldn't allocate PVR PCI structure"));
return IMG_NULL;
}
psPVRPCI->psPCIDev = (struct pci_dev *)pvPCICookie;
psPVRPCI->ePCIFlags = eFlags;
err = pci_enable_device(psPVRPCI->psPCIDev);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCISetDev: Couldn't enable device (%d)", err));
return IMG_NULL;
}
if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)
{
pci_set_master(psPVRPCI->psPCIDev);
}
if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI)
{
#if defined(CONFIG_PCI_MSI)
err = pci_enable_msi(psPVRPCI->psPCIDev);
if (err != 0)
{
PVR_DPF((PVR_DBG_WARNING, "OSPCISetDev: Couldn't enable MSI (%d)", err));
psPVRPCI->ePCIFlags &= ~HOST_PCI_INIT_FLAG_MSI;
}
#else
PVR_DPF((PVR_DBG_WARNING, "OSPCISetDev: MSI support not enabled in the kernel"));
#endif
}
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
{
psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
}
return (PVRSRV_PCI_DEV_HANDLE)psPVRPCI;
}
PVRSRV_PCI_DEV_HANDLE OSPCIAcquireDev(IMG_UINT16 ui16VendorID, IMG_UINT16 ui16DeviceID, HOST_PCI_INIT_FLAGS eFlags)
{
struct pci_dev *psPCIDev;
psPCIDev = pci_get_device(ui16VendorID, ui16DeviceID, NULL);
if (psPCIDev == NULL)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIAcquireDev: Couldn't acquire device"));
return IMG_NULL;
}
return OSPCISetDev((IMG_VOID *)psPCIDev, eFlags);
}
PVRSRV_ERROR OSPCIIRQ(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 *pui32IRQ)
{
PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
*pui32IRQ = psPVRPCI->psPCIDev->irq;
return PVRSRV_OK;
}
enum HOST_PCI_ADDR_RANGE_FUNC
{
HOST_PCI_ADDR_RANGE_FUNC_LEN,
HOST_PCI_ADDR_RANGE_FUNC_START,
HOST_PCI_ADDR_RANGE_FUNC_END,
HOST_PCI_ADDR_RANGE_FUNC_REQUEST,
HOST_PCI_ADDR_RANGE_FUNC_RELEASE
};
static IMG_UINT32 OSPCIAddrRangeFunc(enum HOST_PCI_ADDR_RANGE_FUNC eFunc,
PVRSRV_PCI_DEV_HANDLE hPVRPCI,
IMG_UINT32 ui32Index)
{
PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
if (ui32Index >= DEVICE_COUNT_RESOURCE)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: Index out of range"));
return 0;
}
switch (eFunc)
{
case HOST_PCI_ADDR_RANGE_FUNC_LEN:
return pci_resource_len(psPVRPCI->psPCIDev, ui32Index);
case HOST_PCI_ADDR_RANGE_FUNC_START:
return pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
case HOST_PCI_ADDR_RANGE_FUNC_END:
return pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
case HOST_PCI_ADDR_RANGE_FUNC_REQUEST:
{
int err;
err = pci_request_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index, PVRSRV_MODNAME);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: pci_request_region_failed (%d)", err));
return 0;
}
psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_TRUE;
return 1;
}
case HOST_PCI_ADDR_RANGE_FUNC_RELEASE:
if (psPVRPCI->abPCIResourceInUse[ui32Index])
{
pci_release_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index);
psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_FALSE;
}
return 1;
default:
PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: Unknown function"));
break;
}
return 0;
}
IMG_UINT32 OSPCIAddrRangeLen(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
{
return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_LEN, hPVRPCI, ui32Index);
}
IMG_UINT32 OSPCIAddrRangeStart(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
{
return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_START, hPVRPCI, ui32Index);
}
IMG_UINT32 OSPCIAddrRangeEnd(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
{
return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_END, hPVRPCI, ui32Index);
}
PVRSRV_ERROR OSPCIRequestAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI,
IMG_UINT32 ui32Index)
{
return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_REQUEST, hPVRPCI, ui32Index) == 0 ? PVRSRV_ERROR_PCI_CALL_FAILED : PVRSRV_OK;
}
PVRSRV_ERROR OSPCIReleaseAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
{
return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_RELEASE, hPVRPCI, ui32Index) == 0 ? PVRSRV_ERROR_PCI_CALL_FAILED : PVRSRV_OK;
}
PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
{
PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
int i;
PVR_TRACE(("OSPCIReleaseDev"));
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
{
if (psPVRPCI->abPCIResourceInUse[i])
{
PVR_TRACE(("OSPCIReleaseDev: Releasing Address range %d", i));
pci_release_region(psPVRPCI->psPCIDev, i);
psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
}
}
#if defined(CONFIG_PCI_MSI)
if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI)
{
pci_disable_msi(psPVRPCI->psPCIDev);
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)
{
pci_clear_master(psPVRPCI->psPCIDev);
}
#endif
pci_disable_device(psPVRPCI->psPCIDev);
OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psPVRPCI), (IMG_VOID *)psPVRPCI, IMG_NULL);
return PVRSRV_OK;
}
PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
{
PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
int i;
int err;
PVR_TRACE(("OSPCISuspendDev"));
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
{
if (psPVRPCI->abPCIResourceInUse[i])
{
pci_release_region(psPVRPCI->psPCIDev, i);
}
}
err = pci_save_state(psPVRPCI->psPCIDev);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: pci_save_state_failed (%d)", err));
return PVRSRV_ERROR_PCI_CALL_FAILED;
}
pci_disable_device(psPVRPCI->psPCIDev);
err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_SUSPEND));
switch(err)
{
case 0:
break;
case -EIO:
PVR_DPF((PVR_DBG_WARNING, "OSPCISuspendDev: device doesn't support PCI PM"));
break;
case -EINVAL:
PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: can't enter requested power state"));
break;
default:
PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: pci_set_power_state failed (%d)", err));
break;
}
return PVRSRV_OK;
}
PVRSRV_ERROR OSPCIResumeDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
{
PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
int err;
int i;
PVR_TRACE(("OSPCIResumeDev"));
err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_ON));
switch(err)
{
case 0:
break;
case -EIO:
PVR_DPF((PVR_DBG_WARNING, "OSPCIResumeDev: device doesn't support PCI PM"));
break;
case -EINVAL:
PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: can't enter requested power state"));
return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
default:
PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_set_power_state failed (%d)", err));
return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
pci_restore_state(psPVRPCI->psPCIDev);
#else
err = pci_restore_state(psPVRPCI->psPCIDev);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_restore_state failed (%d)", err));
return PVRSRV_ERROR_PCI_CALL_FAILED;
}
#endif
err = pci_enable_device(psPVRPCI->psPCIDev);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: Couldn't enable device (%d)", err));
return PVRSRV_ERROR_PCI_CALL_FAILED;
}
if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)
pci_set_master(psPVRPCI->psPCIDev);
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
{
if (psPVRPCI->abPCIResourceInUse[i])
{
err = pci_request_region(psPVRPCI->psPCIDev, i, PVRSRV_MODNAME);
if (err != 0)
{
PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_request_region_failed (region %d, error %d)", i, err));
}
}
}
return PVRSRV_OK;
}
#endif
#define OS_MAX_TIMERS 8
typedef struct TIMER_CALLBACK_DATA_TAG
{
IMG_BOOL bInUse;
PFN_TIMER_FUNC pfnTimerFunc;
IMG_VOID *pvData;
struct timer_list sTimer;
IMG_UINT32 ui32Delay;
IMG_BOOL bActive;
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
struct work_struct sWork;
#endif
}TIMER_CALLBACK_DATA;
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
static struct workqueue_struct *psTimerWorkQueue;
#endif
static TIMER_CALLBACK_DATA sTimers[OS_MAX_TIMERS];
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
DEFINE_MUTEX(sTimerStructLock);
#else
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
static spinlock_t sTimerStructLock = SPIN_LOCK_UNLOCKED;
#else
static DEFINE_SPINLOCK(sTimerStructLock);
#endif
#endif
static void OSTimerCallbackBody(TIMER_CALLBACK_DATA *psTimerCBData)
{
if (!psTimerCBData->bActive)
return;
psTimerCBData->pfnTimerFunc(psTimerCBData->pvData);
mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies);
}
static IMG_VOID OSTimerCallbackWrapper(IMG_UINT32 ui32Data)
{
TIMER_CALLBACK_DATA *psTimerCBData = (TIMER_CALLBACK_DATA*)ui32Data;
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
int res;
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
res = queue_work(psTimerWorkQueue, &psTimerCBData->sWork);
#else
res = schedule_work(&psTimerCBData->sWork);
#endif
if (res == 0)
{
PVR_DPF((PVR_DBG_WARNING, "OSTimerCallbackWrapper: work already queued"));
}
#else
OSTimerCallbackBody(psTimerCBData);
#endif
}
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
static void OSTimerWorkQueueCallBack(struct work_struct *psWork)
{
TIMER_CALLBACK_DATA *psTimerCBData = container_of(psWork, TIMER_CALLBACK_DATA, sWork);
OSTimerCallbackBody(psTimerCBData);
}
#endif
IMG_HANDLE OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, IMG_VOID *pvData, IMG_UINT32 ui32MsTimeout)
{
TIMER_CALLBACK_DATA *psTimerCBData;
IMG_UINT32 ui32i;
#if !(defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE))
unsigned long ulLockFlags;
#endif
if(!pfnTimerFunc)
{
PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: passed invalid callback"));
return IMG_NULL;
}
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
mutex_lock(&sTimerStructLock);
#else
spin_lock_irqsave(&sTimerStructLock, ulLockFlags);
#endif
for (ui32i = 0; ui32i < OS_MAX_TIMERS; ui32i++)
{
psTimerCBData = &sTimers[ui32i];
if (!psTimerCBData->bInUse)
{
psTimerCBData->bInUse = IMG_TRUE;
break;
}
}
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
mutex_unlock(&sTimerStructLock);
#else
spin_unlock_irqrestore(&sTimerStructLock, ulLockFlags);
#endif
if (ui32i >= OS_MAX_TIMERS)
{
PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: all timers are in use"));
return IMG_NULL;
}
psTimerCBData->pfnTimerFunc = pfnTimerFunc;
psTimerCBData->pvData = pvData;
psTimerCBData->bActive = IMG_FALSE;
psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000)
? 1
: ((HZ * ui32MsTimeout) / 1000);
init_timer(&psTimerCBData->sTimer);
psTimerCBData->sTimer.function = (IMG_VOID *)OSTimerCallbackWrapper;
psTimerCBData->sTimer.data = (IMG_UINT32)psTimerCBData;
return (IMG_HANDLE)(ui32i + 1);
}
static inline TIMER_CALLBACK_DATA *GetTimerStructure(IMG_HANDLE hTimer)
{
IMG_UINT32 ui32i = ((IMG_UINT32)hTimer) - 1;
PVR_ASSERT(ui32i < OS_MAX_TIMERS);
return &sTimers[ui32i];
}
PVRSRV_ERROR OSRemoveTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(!psTimerCBData->bActive);
psTimerCBData->bInUse = IMG_FALSE;
return PVRSRV_OK;
}
PVRSRV_ERROR OSEnableTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(!psTimerCBData->bActive);
psTimerCBData->bActive = IMG_TRUE;
psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies;
add_timer(&psTimerCBData->sTimer);
return PVRSRV_OK;
}
PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(psTimerCBData->bActive);
psTimerCBData->bActive = IMG_FALSE;
smp_mb();
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
flush_workqueue(psTimerWorkQueue);
#endif
#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
flush_scheduled_work();
#endif
del_timer_sync(&psTimerCBData->sTimer);
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
flush_workqueue(psTimerWorkQueue);
#endif
#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
flush_scheduled_work();
#endif
return PVRSRV_OK;
}
#if defined (SUPPORT_SID_INTERFACE)
PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT_KM *psEventObject)
#else
PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT *psEventObject)
#endif
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(psEventObject)
{
if(pszName)
{
strncpy(psEventObject->szName, pszName, EVENTOBJNAME_MAXLENGTH);
}
else
{
static IMG_UINT16 ui16NameIndex = 0;
#if defined (SUPPORT_SID_INTERFACE)
snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH, "PVRSRV_EVENTOBJECT_KM_%d", ui16NameIndex++);
#else
snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH, "PVRSRV_EVENTOBJECT_%d", ui16NameIndex++);
#endif
}
if(LinuxEventObjectListCreate(&psEventObject->hOSEventKM) != PVRSRV_OK)
{
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectCreateKM: psEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_UNABLE_TO_CREATE_EVENT;
}
return eError;
}
#if defined (SUPPORT_SID_INTERFACE)
PVRSRV_ERROR OSEventObjectDestroyKM(PVRSRV_EVENTOBJECT_KM *psEventObject)
#else
PVRSRV_ERROR OSEventObjectDestroyKM(PVRSRV_EVENTOBJECT *psEventObject)
#endif
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(psEventObject)
{
if(psEventObject->hOSEventKM)
{
LinuxEventObjectListDestroy(psEventObject->hOSEventKM);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: hOSEventKM is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: psEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
PVRSRV_ERROR OSEventObjectWaitKM(IMG_HANDLE hOSEventKM)
{
PVRSRV_ERROR eError;
if(hOSEventKM)
{
eError = LinuxEventObjectWait(hOSEventKM, EVENT_OBJECT_TIMEOUT_MS);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectWaitKM: hOSEventKM is not a valid handle"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
#if defined (SUPPORT_SID_INTERFACE)
PVRSRV_ERROR OSEventObjectOpenKM(PVRSRV_EVENTOBJECT_KM *psEventObject,
#else
PVRSRV_ERROR OSEventObjectOpenKM(PVRSRV_EVENTOBJECT *psEventObject,
#endif
IMG_HANDLE *phOSEvent)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(psEventObject)
{
if(LinuxEventObjectAdd(psEventObject->hOSEventKM, phOSEvent) != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: failed"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectCreateKM: psEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
#if defined (SUPPORT_SID_INTERFACE)
PVRSRV_ERROR OSEventObjectCloseKM(PVRSRV_EVENTOBJECT_KM *psEventObject,
#else
PVRSRV_ERROR OSEventObjectCloseKM(PVRSRV_EVENTOBJECT *psEventObject,
#endif
IMG_HANDLE hOSEventKM)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(psEventObject)
{
if(LinuxEventObjectDelete(psEventObject->hOSEventKM, hOSEventKM) != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectDelete: failed"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: psEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM)
{
PVRSRV_ERROR eError;
if(hOSEventKM)
{
eError = LinuxEventObjectSignal(hOSEventKM);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectSignalKM: hOSEventKM is not a valid handle"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID)
{
return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE;
}
PVRSRV_ERROR OSCopyToUser(IMG_PVOID pvProcess,
IMG_VOID *pvDest,
IMG_VOID *pvSrc,
IMG_UINT32 ui32Bytes)
{
PVR_UNREFERENCED_PARAMETER(pvProcess);
if(pvr_copy_to_user(pvDest, pvSrc, ui32Bytes)==0)
return PVRSRV_OK;
else
return PVRSRV_ERROR_FAILED_TO_COPY_VIRT_MEMORY;
}
PVRSRV_ERROR OSCopyFromUser( IMG_PVOID pvProcess,
IMG_VOID *pvDest,
IMG_VOID *pvSrc,
IMG_UINT32 ui32Bytes)
{
PVR_UNREFERENCED_PARAMETER(pvProcess);
if(pvr_copy_from_user(pvDest, pvSrc, ui32Bytes)==0)
return PVRSRV_OK;
else
return PVRSRV_ERROR_FAILED_TO_COPY_VIRT_MEMORY;
}
IMG_BOOL OSAccessOK(IMG_VERIFY_TEST eVerification, IMG_VOID *pvUserPtr, IMG_UINT32 ui32Bytes)
{
IMG_INT linuxType;
if (eVerification == PVR_VERIFY_READ)
{
linuxType = VERIFY_READ;
}
else
{
PVR_ASSERT(eVerification == PVR_VERIFY_WRITE);
linuxType = VERIFY_WRITE;
}
return access_ok(linuxType, pvUserPtr, ui32Bytes);
}
typedef enum _eWrapMemType_
{
WRAP_TYPE_NULL = 0,
WRAP_TYPE_GET_USER_PAGES,
WRAP_TYPE_FIND_VMA
} eWrapMemType;
typedef struct _sWrapMemInfo_
{
eWrapMemType eType;
IMG_INT iNumPages;
IMG_INT iNumPagesMapped;
struct page **ppsPages;
IMG_SYS_PHYADDR *psPhysAddr;
IMG_INT iPageOffset;
#if defined(DEBUG)
IMG_UINT32 ulStartAddr;
IMG_UINT32 ulBeyondEndAddr;
struct vm_area_struct *psVMArea;
#endif
} sWrapMemInfo;
static IMG_BOOL CPUVAddrToPFN(struct vm_area_struct *psVMArea, IMG_UINT32 ulCPUVAddr, IMG_UINT32 *pulPFN, struct page **ppsPage)
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10))
pgd_t *psPGD;
pud_t *psPUD;
pmd_t *psPMD;
pte_t *psPTE;
struct mm_struct *psMM = psVMArea->vm_mm;
spinlock_t *psPTLock;
IMG_BOOL bRet = IMG_FALSE;
*pulPFN = 0;
*ppsPage = NULL;
psPGD = pgd_offset(psMM, ulCPUVAddr);
if (pgd_none(*psPGD) || pgd_bad(*psPGD))
return bRet;
psPUD = pud_offset(psPGD, ulCPUVAddr);
if (pud_none(*psPUD) || pud_bad(*psPUD))
return bRet;
psPMD = pmd_offset(psPUD, ulCPUVAddr);
if (pmd_none(*psPMD) || pmd_bad(*psPMD))
return bRet;
psPTE = (pte_t *)pte_offset_map_lock(psMM, psPMD, ulCPUVAddr, &psPTLock);
if ((pte_none(*psPTE) == 0) && (pte_present(*psPTE) != 0) && (pte_write(*psPTE) != 0))
{
*pulPFN = pte_pfn(*psPTE);
bRet = IMG_TRUE;
if (pfn_valid(*pulPFN))
{
*ppsPage = pfn_to_page(*pulPFN);
get_page(*ppsPage);
}
}
pte_unmap_unlock(psPTE, psPTLock);
return bRet;
#else
return IMG_FALSE;
#endif
}
PVRSRV_ERROR OSReleasePhysPageAddr(IMG_HANDLE hOSWrapMem)
{
sWrapMemInfo *psInfo = (sWrapMemInfo *)hOSWrapMem;
IMG_INT i;
if (psInfo == IMG_NULL)
{
PVR_DPF((PVR_DBG_WARNING,
"OSReleasePhysPageAddr: called with null wrap handle"));
return PVRSRV_OK;
}
switch (psInfo->eType)
{
case WRAP_TYPE_NULL:
{
PVR_DPF((PVR_DBG_WARNING,
"OSReleasePhysPageAddr: called with wrap type WRAP_TYPE_NULL"));
break;
}
case WRAP_TYPE_GET_USER_PAGES:
{
for (i = 0; i < psInfo->iNumPagesMapped; i++)
{
struct page *psPage = psInfo->ppsPages[i];
PVR_ASSERT(psPage != NULL);
if (psInfo->iNumPagesMapped == psInfo->iNumPages)
{
if (!PageReserved(psPage))
{
SetPageDirty(psPage);
}
}
page_cache_release(psPage);
}
break;
}
case WRAP_TYPE_FIND_VMA:
{
for (i = 0; i < psInfo->iNumPages; i++)
{
if (psInfo->ppsPages[i] != IMG_NULL)
{
put_page(psInfo->ppsPages[i]);
}
}
break;
}
default:
{
PVR_DPF((PVR_DBG_ERROR,
"OSReleasePhysPageAddr: Unknown wrap type (%d)", psInfo->eType));
return PVRSRV_ERROR_INVALID_WRAP_TYPE;
}
}
if (psInfo->ppsPages != IMG_NULL)
{
kfree(psInfo->ppsPages);
}
if (psInfo->psPhysAddr != IMG_NULL)
{
kfree(psInfo->psPhysAddr);
}
kfree(psInfo);
return PVRSRV_OK;
}
#if defined(CONFIG_TI_TILER)
static IMG_UINT32 CPUAddrToTilerPhy(IMG_UINT32 uiAddr)
{
IMG_UINT32 ui32PhysAddr = 0;
pte_t *ptep, pte;
pgd_t *pgd;
pmd_t *pmd;
pgd = pgd_offset(current->mm, uiAddr);
if (pgd_none(*pgd) || pgd_bad(*pgd))
goto err_out;
pmd = pmd_offset(pgd, uiAddr);
if (pmd_none(*pmd) || pmd_bad(*pmd))
goto err_out;
ptep = pte_offset_map(pmd, uiAddr);
if (!ptep)
goto err_out;
pte = *ptep;
if (!pte_present(pte))
goto err_out;
ui32PhysAddr = (pte & PAGE_MASK) | (~PAGE_MASK & uiAddr);
if (ui32PhysAddr < 0x60000000 && ui32PhysAddr > 0x7fffffff)
{
PVR_DPF((PVR_DBG_ERROR, "CPUAddrToTilerPhy: Not in tiler range"));
ui32PhysAddr = 0;
goto err_out;
}
err_out:
return ui32PhysAddr;
}
#endif
PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr,
IMG_UINT32 ui32Bytes,
IMG_SYS_PHYADDR *psSysPAddr,
IMG_HANDLE *phOSWrapMem)
{
IMG_UINT32 ulStartAddrOrig = (IMG_UINT32) pvCPUVAddr;
IMG_UINT32 ulAddrRangeOrig = (IMG_UINT32) ui32Bytes;
IMG_UINT32 ulBeyondEndAddrOrig = ulStartAddrOrig + ulAddrRangeOrig;
IMG_UINT32 ulStartAddr;
IMG_UINT32 ulAddrRange;
IMG_UINT32 ulBeyondEndAddr;
IMG_UINT32 ulAddr;
IMG_INT i;
struct vm_area_struct *psVMArea;
sWrapMemInfo *psInfo = NULL;
IMG_BOOL bHavePageStructs = IMG_FALSE;
IMG_BOOL bHaveNoPageStructs = IMG_FALSE;
IMG_BOOL bMMapSemHeld = IMG_FALSE;
PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY;
ulStartAddr = ulStartAddrOrig & PAGE_MASK;
ulBeyondEndAddr = PAGE_ALIGN(ulBeyondEndAddrOrig);
ulAddrRange = ulBeyondEndAddr - ulStartAddr;
if (ulBeyondEndAddr <= ulStartAddr)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Invalid address range (start %x, length %x)",
ulStartAddrOrig, ulAddrRangeOrig));
goto error;
}
psInfo = kmalloc(sizeof(*psInfo), GFP_KERNEL);
if (psInfo == NULL)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Couldn't allocate information structure"));
goto error;
}
memset(psInfo, 0, sizeof(*psInfo));
#if defined(DEBUG)
psInfo->ulStartAddr = ulStartAddrOrig;
psInfo->ulBeyondEndAddr = ulBeyondEndAddrOrig;
#endif
psInfo->iNumPages = (IMG_INT)(ulAddrRange >> PAGE_SHIFT);
psInfo->iPageOffset = (IMG_INT)(ulStartAddrOrig & ~PAGE_MASK);
psInfo->psPhysAddr = kmalloc((size_t)psInfo->iNumPages * sizeof(*psInfo->psPhysAddr), GFP_KERNEL);
if (psInfo->psPhysAddr == NULL)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Couldn't allocate page array"));
goto error;
}
memset(psInfo->psPhysAddr, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->psPhysAddr));
psInfo->ppsPages = kmalloc((size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages), GFP_KERNEL);
if (psInfo->ppsPages == NULL)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Couldn't allocate page array"));
goto error;
}
memset(psInfo->ppsPages, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages));
eError = PVRSRV_ERROR_BAD_MAPPING;
psInfo->eType = WRAP_TYPE_GET_USER_PAGES;
down_read(&current->mm->mmap_sem);
bMMapSemHeld = IMG_TRUE;
psInfo->iNumPagesMapped = get_user_pages(current, current->mm, ulStartAddr, psInfo->iNumPages, 1, 0, psInfo->ppsPages, NULL);
if (psInfo->iNumPagesMapped >= 0)
{
if (psInfo->iNumPagesMapped != psInfo->iNumPages)
{
PVR_TRACE(("OSAcquirePhysPageAddr: Couldn't map all the pages needed (wanted: %d, got %d)", psInfo->iNumPages, psInfo->iNumPagesMapped));
goto error;
}
for (i = 0; i < psInfo->iNumPages; i++)
{
IMG_CPU_PHYADDR CPUPhysAddr;
IMG_UINT32 ulPFN;
ulPFN = page_to_pfn(psInfo->ppsPages[i]);
CPUPhysAddr.uiAddr = ulPFN << PAGE_SHIFT;
if ((CPUPhysAddr.uiAddr >> PAGE_SHIFT) != ulPFN)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Page frame number out of range (%x)", ulPFN));
goto error;
}
psInfo->psPhysAddr[i] = SysCpuPAddrToSysPAddr(CPUPhysAddr);
psSysPAddr[i] = psInfo->psPhysAddr[i];
}
goto exit;
}
PVR_DPF((PVR_DBG_MESSAGE, "OSAcquirePhysPageAddr: get_user_pages failed (%d), using CPU page table", psInfo->iNumPagesMapped));
psInfo->eType = WRAP_TYPE_NULL;
psInfo->iNumPagesMapped = 0;
memset(psInfo->ppsPages, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages));
psInfo->eType = WRAP_TYPE_FIND_VMA;
psVMArea = find_vma(current->mm, ulStartAddrOrig);
if (psVMArea == NULL)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Couldn't find memory region containing start address %x", ulStartAddrOrig));
goto error;
}
#if defined(DEBUG)
psInfo->psVMArea = psVMArea;
#endif
if (ulStartAddrOrig < psVMArea->vm_start)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Start address %x is outside of the region returned by find_vma", ulStartAddrOrig));
goto error;
}
if (ulBeyondEndAddrOrig > psVMArea->vm_end)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: End address %x is outside of the region returned by find_vma", ulBeyondEndAddrOrig));
goto error;
}
if ((psVMArea->vm_flags & (VM_IO | VM_RESERVED)) != (VM_IO | VM_RESERVED))
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Memory region does not represent memory mapped I/O (VMA flags: 0x%lx)", psVMArea->vm_flags));
goto error;
}
if ((psVMArea->vm_flags & (VM_READ | VM_WRITE)) != (VM_READ | VM_WRITE))
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: No read/write access to memory region (VMA flags: 0x%lx)", psVMArea->vm_flags));
goto error;
}
for (ulAddr = ulStartAddrOrig, i = 0; ulAddr < ulBeyondEndAddrOrig; ulAddr += PAGE_SIZE, i++)
{
IMG_CPU_PHYADDR CPUPhysAddr;
IMG_UINT32 ulPFN = 0;
PVR_ASSERT(i < psInfo->iNumPages);
if (!CPUVAddrToPFN(psVMArea, ulAddr, &ulPFN, &psInfo->ppsPages[i]))
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Invalid CPU virtual address"));
goto error;
}
if (psInfo->ppsPages[i] == NULL)
{
#if defined(CONFIG_TI_TILER)
IMG_UINT32 ui32TilerAddr = CPUAddrToTilerPhy(ulAddr);
if (ui32TilerAddr)
{
bHavePageStructs = IMG_TRUE;
psInfo->iNumPagesMapped++;
psInfo->psPhysAddr[i].uiAddr = ui32TilerAddr;
psSysPAddr[i].uiAddr = ui32TilerAddr;
continue;
}
#endif
bHaveNoPageStructs = IMG_TRUE;
}
else
{
bHavePageStructs = IMG_TRUE;
psInfo->iNumPagesMapped++;
PVR_ASSERT(ulPFN == page_to_pfn(psInfo->ppsPages[i]));
}
CPUPhysAddr.uiAddr = ulPFN << PAGE_SHIFT;
if ((CPUPhysAddr.uiAddr >> PAGE_SHIFT) != ulPFN)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Page frame number out of range (%x)", ulPFN));
goto error;
}
psInfo->psPhysAddr[i] = SysCpuPAddrToSysPAddr(CPUPhysAddr);
psSysPAddr[i] = psInfo->psPhysAddr[i];
}
PVR_ASSERT(i == psInfo->iNumPages);
#if defined(VM_MIXEDMAP)
if ((psVMArea->vm_flags & VM_MIXEDMAP) != 0)
{
goto exit;
}
#endif
if (bHavePageStructs && bHaveNoPageStructs)
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Region is VM_MIXEDMAP, but isn't marked as such"));
goto error;
}
if (!bHaveNoPageStructs)
{
goto exit;
}
#if defined(VM_PFNMAP)
if ((psVMArea->vm_flags & VM_PFNMAP) == 0)
#endif
{
PVR_DPF((PVR_DBG_ERROR,
"OSAcquirePhysPageAddr: Region is VM_PFNMAP, but isn't marked as such"));
goto error;
}
exit:
PVR_ASSERT(bMMapSemHeld);
up_read(&current->mm->mmap_sem);
*phOSWrapMem = (IMG_HANDLE)psInfo;
if (bHaveNoPageStructs)
{
PVR_DPF((PVR_DBG_MESSAGE,
"OSAcquirePhysPageAddr: Region contains pages which can't be locked down (no page structures)"));
}
PVR_ASSERT(psInfo->eType != 0);
#if 0
OSCleanCPUCacheRangeKM(pvCPUVAddr, (IMG_VOID *)((IMG_CHAR *)pvCPUVAddr + ui32Bytes));
#endif
return PVRSRV_OK;
error:
if (bMMapSemHeld)
{
up_read(&current->mm->mmap_sem);
}
OSReleasePhysPageAddr((IMG_HANDLE)psInfo);
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
typedef void (*InnerCacheOp_t)(const void *pvStart, const void *pvEnd);
#if defined(__arm__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
typedef void (*OuterCacheOp_t)(phys_addr_t uStart, phys_addr_t uEnd);
#else
typedef void (*OuterCacheOp_t)(unsigned long ulStart, unsigned long ulEnd);
#endif
#if defined(CONFIG_OUTER_CACHE)
typedef unsigned long (*MemAreaToPhys_t)(LinuxMemArea *psLinuxMemArea,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32PageNumOffset,
IMG_UINT32 ui32PageNum);
static unsigned long VMallocAreaToPhys(LinuxMemArea *psLinuxMemArea,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32PageNumOffset,
IMG_UINT32 ui32PageNum)
{
return vmalloc_to_pfn(pvRangeAddrStart + ui32PageNum * PAGE_SIZE) << PAGE_SHIFT;
}
static unsigned long ExternalKVAreaToPhys(LinuxMemArea *psLinuxMemArea,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32PageNumOffset,
IMG_UINT32 ui32PageNum)
{
IMG_SYS_PHYADDR SysPAddr;
IMG_CPU_PHYADDR CpuPAddr;
SysPAddr = psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr[ui32PageNumOffset + ui32PageNum];
CpuPAddr = SysSysPAddrToCpuPAddr(SysPAddr);
return CpuPAddr.uiAddr;
}
static unsigned long AllocPagesAreaToPhys(LinuxMemArea *psLinuxMemArea,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32PageNumOffset,
IMG_UINT32 ui32PageNum)
{
struct page *pPage;
pPage = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageNumOffset + ui32PageNum];
return page_to_pfn(pPage) << PAGE_SHIFT;
}
static unsigned long IONAreaToPhys(LinuxMemArea *psLinuxMemArea,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32PageNumOffset,
IMG_UINT32 ui32PageNum)
{
IMG_CPU_PHYADDR CpuPAddr;
CpuPAddr = psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs[ui32PageNumOffset + ui32PageNum];
return CpuPAddr.uiAddr;
}
#endif
static
IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList,
IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length)
{
PKV_OFFSET_STRUCT psOffsetStruct;
IMG_VOID *pvMinVAddr;
list_for_each_entry(psOffsetStruct, psMMapOffsetStructList, sAreaItem)
{
if(OSGetCurrentProcessIDKM() != psOffsetStruct->ui32PID)
continue;
pvMinVAddr = (IMG_VOID *)psOffsetStruct->ui32UserVAddr;
if(pvRangeAddrStart >= pvMinVAddr &&
ui32Length <= psOffsetStruct->ui32RealByteSize)
return pvMinVAddr;
}
return IMG_NULL;
}
extern PVRSRV_LINUX_MUTEX g_sMMapMutex;
static
IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length,
InnerCacheOp_t pfnInnerCacheOp,
OuterCacheOp_t pfnOuterCacheOp)
{
LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
IMG_UINT32 ui32AreaLength, ui32AreaOffset = 0;
struct list_head *psMMapOffsetStructList;
IMG_VOID *pvMinVAddr;
#if defined(CONFIG_OUTER_CACHE)
MemAreaToPhys_t pfnMemAreaToPhys = IMG_NULL;
IMG_UINT32 ui32PageNumOffset = 0;
#endif
PVR_ASSERT(psLinuxMemArea != IMG_NULL);
LinuxLockMutex(&g_sMMapMutex);
psMMapOffsetStructList = &psLinuxMemArea->sMMapOffsetStructList;
ui32AreaLength = psLinuxMemArea->ui32ByteSize;
PVR_ASSERT(ui32Length <= ui32AreaLength);
if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC)
{
ui32AreaOffset = psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset;
psLinuxMemArea = psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea;
}
PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC);
switch(psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_VMALLOC:
{
if(is_vmalloc_addr(pvRangeAddrStart))
{
pvMinVAddr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress + ui32AreaOffset;
if(pvRangeAddrStart < pvMinVAddr)
goto err_blocked;
pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length);
}
else
{
pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList,
pvRangeAddrStart, ui32Length);
if(!pvMinVAddr)
goto err_blocked;
pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length);
#if defined(CONFIG_OUTER_CACHE)
pvRangeAddrStart = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress +
(ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr);
}
pfnMemAreaToPhys = VMallocAreaToPhys;
#else
}
#endif
break;
}
case LINUX_MEM_AREA_EXTERNAL_KV:
{
if (psLinuxMemArea->uData.sExternalKV.bPhysContig == IMG_TRUE)
{
PVR_DPF((PVR_DBG_WARNING, "%s: Attempt to flush contiguous external memory", __func__));
goto err_blocked;
}
if (psLinuxMemArea->uData.sExternalKV.pvExternalKV != IMG_NULL)
{
PVR_DPF((PVR_DBG_WARNING, "%s: Attempt to flush external memory with a kernel virtual address", __func__));
goto err_blocked;
}
pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList,
pvRangeAddrStart, ui32Length);
if(!pvMinVAddr)
goto err_blocked;
pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length);
#if defined(CONFIG_OUTER_CACHE)
ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT;
pfnMemAreaToPhys = ExternalKVAreaToPhys;
#endif
break;
}
case LINUX_MEM_AREA_ION:
{
pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList,
pvRangeAddrStart, ui32Length);
if(!pvMinVAddr)
goto err_blocked;
pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length);
#if defined(CONFIG_OUTER_CACHE)
ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT;
pfnMemAreaToPhys = IONAreaToPhys;
#endif
break;
}
case LINUX_MEM_AREA_ALLOC_PAGES:
{
pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList,
pvRangeAddrStart, ui32Length);
if(!pvMinVAddr)
goto err_blocked;
pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length);
#if defined(CONFIG_OUTER_CACHE)
ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT;
pfnMemAreaToPhys = AllocPagesAreaToPhys;
#endif
break;
}
default:
PVR_DBG_BREAK;
}
LinuxUnLockMutex(&g_sMMapMutex);
#if defined(CONFIG_OUTER_CACHE)
PVR_ASSERT(pfnMemAreaToPhys != IMG_NULL);
{
unsigned long ulStart, ulEnd, ulLength, ulStartOffset, ulEndOffset;
IMG_UINT32 i, ui32NumPages;
ulLength = (unsigned long)ui32Length;
ulStartOffset = ((unsigned long)pvRangeAddrStart) & (PAGE_SIZE - 1);
ulEndOffset = ((unsigned long)pvRangeAddrStart + ulLength) & (PAGE_SIZE - 1);
ui32NumPages = (ulStartOffset + ulLength + PAGE_SIZE - 1) >> PAGE_SHIFT;
for(i = 0; i < ui32NumPages; i++)
{
ulStart = pfnMemAreaToPhys(psLinuxMemArea, pvRangeAddrStart,
ui32PageNumOffset, i);
ulEnd = ulStart + PAGE_SIZE;
if(i == ui32NumPages - 1 && ulEndOffset != 0)
ulEnd = ulStart + ulEndOffset;
if(i == 0)
ulStart += ulStartOffset;
pfnOuterCacheOp(ulStart, ulEnd);
}
}
#endif
return IMG_TRUE;
err_blocked:
PVR_DPF((PVR_DBG_WARNING, "%s: Blocked cache op on virtual range "
"%p-%p (type %d)", __func__,
pvRangeAddrStart, pvRangeAddrStart + ui32Length,
psLinuxMemArea->eAreaType));
LinuxUnLockMutex(&g_sMMapMutex);
return IMG_FALSE;
}
#if defined(__i386__)
#define ROUND_UP(x,a) (((x) + (a) - 1) & ~((a) - 1))
static void per_cpu_cache_flush(void *arg)
{
PVR_UNREFERENCED_PARAMETER(arg);
wbinvd();
}
static void x86_flush_cache_range(const void *pvStart, const void *pvEnd)
{
IMG_BYTE *pbStart = (IMG_BYTE *)pvStart;
IMG_BYTE *pbEnd = (IMG_BYTE *)pvEnd;
IMG_BYTE *pbBase;
pbEnd = (IMG_BYTE *)ROUND_UP((IMG_UINTPTR_T)pbEnd,
boot_cpu_data.x86_clflush_size);
mb();
for(pbBase = pbStart; pbBase < pbEnd; pbBase += boot_cpu_data.x86_clflush_size)
clflush(pbBase);
mb();
}
IMG_VOID OSCleanCPUCacheKM(IMG_VOID)
{
ON_EACH_CPU(per_cpu_cache_flush, NULL, 1);
}
IMG_VOID OSFlushCPUCacheKM(IMG_VOID)
{
ON_EACH_CPU(per_cpu_cache_flush, NULL, 1);
}
IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
x86_flush_cache_range, IMG_NULL);
}
IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
x86_flush_cache_range, IMG_NULL);
}
IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
x86_flush_cache_range, IMG_NULL);
}
#else
#if defined(__arm__)
static void per_cpu_cache_flush(void *arg)
{
PVR_UNREFERENCED_PARAMETER(arg);
flush_cache_all();
}
IMG_VOID OSCleanCPUCacheKM(IMG_VOID)
{
ON_EACH_CPU(per_cpu_cache_flush, NULL, 1);
#if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS)
outer_clean_range(0, ULONG_MAX);
#endif
}
IMG_VOID OSFlushCPUCacheKM(IMG_VOID)
{
ON_EACH_CPU(per_cpu_cache_flush, NULL, 1);
#if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS)
outer_flush_all();
#endif
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
static inline size_t pvr_dmac_range_len(const void *pvStart, const void *pvEnd)
{
return (size_t)((char *)pvEnd - (char *)pvStart);
}
#endif
static void pvr_dmac_inv_range(const void *pvStart, const void *pvEnd)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34))
dmac_inv_range(pvStart, pvEnd);
#else
dmac_map_area(pvStart, pvr_dmac_range_len(pvStart, pvEnd), DMA_FROM_DEVICE);
#endif
}
static void pvr_dmac_clean_range(const void *pvStart, const void *pvEnd)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34))
dmac_clean_range(pvStart, pvEnd);
#else
dmac_map_area(pvStart, pvr_dmac_range_len(pvStart, pvEnd), DMA_TO_DEVICE);
#endif
}
IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
dmac_flush_range, outer_flush_range);
}
IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
pvr_dmac_clean_range, outer_clean_range);
}
IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length,
pvr_dmac_inv_range, outer_inv_range);
}
#else
#if defined(__mips__)
IMG_VOID OSCleanCPUCacheKM(IMG_VOID)
{
dma_cache_wback(0, 0x100000);
}
IMG_VOID OSFlushCPUCacheKM(IMG_VOID)
{
dma_cache_wback_inv(0, 0x100000);
}
IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
if (ui32Length)
dma_cache_wback_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length);
return IMG_TRUE;
}
IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
if (ui32Length)
dma_cache_wback((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length);
return IMG_TRUE;
}
IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle,
IMG_VOID *pvRangeAddrStart,
IMG_UINT32 ui32Length)
{
if (ui32Length)
dma_cache_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length);
return IMG_TRUE;
}
#else
#error "Implement CPU cache flush/clean/invalidate primitives for this CPU!"
#endif
#endif
#endif
typedef struct _AtomicStruct
{
atomic_t RefCount;
} AtomicStruct;
PVRSRV_ERROR OSAtomicAlloc(IMG_PVOID *ppvRefCount)
{
AtomicStruct *psRefCount;
psRefCount = kmalloc(sizeof(AtomicStruct), GFP_KERNEL);
if (psRefCount == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
atomic_set(&psRefCount->RefCount, 0);
*ppvRefCount = psRefCount;
return PVRSRV_OK;
}
IMG_VOID OSAtomicFree(IMG_PVOID pvRefCount)
{
AtomicStruct *psRefCount = pvRefCount;
PVR_ASSERT(atomic_read(&psRefCount->RefCount) == 0);
kfree(psRefCount);
}
IMG_VOID OSAtomicInc(IMG_PVOID pvRefCount)
{
AtomicStruct *psRefCount = pvRefCount;
atomic_inc(&psRefCount->RefCount);
}
IMG_BOOL OSAtomicDecAndTest(IMG_PVOID pvRefCount)
{
AtomicStruct *psRefCount = pvRefCount;
return atomic_dec_and_test(&psRefCount->RefCount) ? IMG_TRUE:IMG_FALSE;
}
IMG_UINT32 OSAtomicRead(IMG_PVOID pvRefCount)
{
AtomicStruct *psRefCount = pvRefCount;
return (IMG_UINT32) atomic_read(&psRefCount->RefCount);
}
IMG_VOID OSReleaseBridgeLock(IMG_VOID)
{
LinuxUnLockMutex(&gPVRSRVLock);
}
IMG_VOID OSReacquireBridgeLock(IMG_VOID)
{
LinuxLockMutex(&gPVRSRVLock);
}
typedef struct _OSTime
{
unsigned long ulTime;
} OSTime;
PVRSRV_ERROR OSTimeCreateWithUSOffset(IMG_PVOID *pvRet, IMG_UINT32 ui32USOffset)
{
OSTime *psOSTime;
psOSTime = kmalloc(sizeof(OSTime), GFP_KERNEL);
if (psOSTime == IMG_NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
psOSTime->ulTime = usecs_to_jiffies(jiffies_to_usecs(jiffies) + ui32USOffset);
*pvRet = psOSTime;
return PVRSRV_OK;
}
IMG_BOOL OSTimeHasTimePassed(IMG_PVOID pvData)
{
OSTime *psOSTime = pvData;
if (time_is_before_jiffies(psOSTime->ulTime))
{
return IMG_TRUE;
}
return IMG_FALSE;
}
IMG_VOID OSTimeDestroy(IMG_PVOID pvData)
{
kfree(pvData);
}
PVRSRV_ERROR PVROSFuncInit(IMG_VOID)
{
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
{
psTimerWorkQueue = create_workqueue("pvr_timer");
if (psTimerWorkQueue == NULL)
{
PVR_DPF((PVR_DBG_ERROR, "%s: couldn't create timer workqueue", __FUNCTION__));
return PVRSRV_ERROR_UNABLE_TO_CREATE_THREAD;
}
}
#endif
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
{
IMG_UINT32 ui32i;
for (ui32i = 0; ui32i < OS_MAX_TIMERS; ui32i++)
{
TIMER_CALLBACK_DATA *psTimerCBData = &sTimers[ui32i];
INIT_WORK(&psTimerCBData->sWork, OSTimerWorkQueueCallBack);
}
}
#endif
return PVRSRV_OK;
}
IMG_VOID PVROSFuncDeInit(IMG_VOID)
{
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
if (psTimerWorkQueue != NULL)
{
destroy_workqueue(psTimerWorkQueue);
}
#endif
}