blob: 0b9079d5bd2e31c6401e5680ec6fa100707e2bcd [file] [log] [blame]
/*************************************************************************/ /*!
@File
@Title Environment related functions
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
@License Dual MIT/GPLv2
The contents of this file are subject to the MIT license as set out below.
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.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 2 ("GPL") in which case the provisions
of GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms of
GPL, and not to allow others to use your version of this file under the terms
of the MIT license, indicate your decision by deleting the provisions above
and replace them with the notice and other provisions required by GPL as set
out in the file called "GPL-COPYING" included in this distribution. If you do
not delete the provisions above, a recipient may use your version of this file
under the terms of either the MIT license or GPL.
This License is also included in this distribution in the file called
"MIT-COPYING".
EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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; AND (B) 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.
*/ /**************************************************************************/
#include <linux/version.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/div64.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/genalloc.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/hardirq.h>
#include <asm/tlbflush.h>
#include <asm/page.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 <linux/kthread.h>
#include <asm/atomic.h>
#include "osfunc.h"
#include "img_types.h"
#include "mm.h"
#include "allocmem.h"
#include "mmap.h"
#include "env_data.h"
#include "pvr_debugfs.h"
#include "event.h"
#include "linkage.h"
#include "pvr_uaccess.h"
#include "pvr_debug.h"
#include "driverlock.h"
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#include "process_stats.h"
#endif
#if defined(SUPPORT_SYSTEM_INTERRUPT_HANDLING)
#include "syscommon.h"
#endif
#if defined(EMULATOR) || defined(VIRTUAL_PLATFORM)
#define EVENT_OBJECT_TIMEOUT_MS (2000)
#else
#define EVENT_OBJECT_TIMEOUT_MS (100)
#endif /* EMULATOR */
ENV_DATA *gpsEnvData = IMG_NULL;
/*
Create a 4MB pool which should be more then enough in most cases,
if it becomes full then the calling code will fall back to
vm_map_ram.
*/
#if defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))
#define POOL_SIZE (4*1024*1024)
static struct gen_pool *pvrsrv_pool_writecombine = NULL;
static char *pool_start;
static void deinit_pvr_pool(void)
{
gen_pool_destroy(pvrsrv_pool_writecombine);
pvrsrv_pool_writecombine = NULL;
vfree(pool_start);
pool_start = NULL;
}
static void init_pvr_pool(void)
{
struct vm_struct *tmp_area;
int ret = -1;
/* Create the pool to allocate vm space from */
pvrsrv_pool_writecombine = gen_pool_create(PAGE_SHIFT, -1);
if (!pvrsrv_pool_writecombine) {
printk(KERN_ERR "%s: create pvrsrv_pool failed\n", __func__);
return;
}
/* Reserve space in the vmalloc vm range */
tmp_area = __get_vm_area(POOL_SIZE, VM_ALLOC,
VMALLOC_START, VMALLOC_END);
if (!tmp_area) {
printk(KERN_ERR "%s: __get_vm_area failed\n", __func__);
gen_pool_destroy(pvrsrv_pool_writecombine);
pvrsrv_pool_writecombine = NULL;
return;
}
pool_start = tmp_area->addr;
if (!pool_start) {
printk(KERN_ERR "%s:No vm space to create POOL\n",
__func__);
gen_pool_destroy(pvrsrv_pool_writecombine);
pvrsrv_pool_writecombine = NULL;
return;
} else {
/* Add our reserved space into the pool */
ret = gen_pool_add(pvrsrv_pool_writecombine,
(unsigned long) pool_start, POOL_SIZE, -1);
if (ret) {
printk(KERN_ERR "%s:could not remainder pool\n",
__func__);
deinit_pvr_pool();
return;
}
}
return;
}
static inline IMG_BOOL vmap_from_pool(void *pvCPUVAddr)
{
IMG_CHAR *pcTmp = pvCPUVAddr;
if ((pcTmp >= pool_start) && (pcTmp <= (pool_start + POOL_SIZE)))
{
return IMG_TRUE;
}
return IMG_FALSE;
}
#endif /* defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))*/
PVRSRV_ERROR OSMMUPxAlloc(PVRSRV_DEVICE_NODE *psDevNode, IMG_SIZE_T uiSize,
Px_HANDLE *psMemHandle, IMG_DEV_PHYADDR *psDevPAddr)
{
IMG_CPU_PHYADDR sCpuPAddr;
struct page *psPage;
/*
Check that we're not doing multiple pages worth of
import as it's not supported a.t.m.
*/
PVR_ASSERT(uiSize == PAGE_SIZE);
psPage = alloc_page(GFP_KERNEL);
if (psPage == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
#if defined (CONFIG_X86)
{
IMG_PVOID pvPageVAddr = page_address(psPage);
int ret;
ret = set_memory_wc((unsigned long)pvPageVAddr, 1);
if (ret)
{
__free_page(psPage);
return PVRSRV_ERROR_UNABLE_TO_SET_CACHE_MODE;
}
}
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined (CONFIG_METAG)
{
IMG_CPU_PHYADDR sCPUPhysAddrStart, sCPUPhysAddrEnd;
IMG_PVOID pvPageVAddr = kmap(psPage);
sCPUPhysAddrStart.uiAddr = page_to_phys(psPage);
sCPUPhysAddrEnd.uiAddr = sCPUPhysAddrStart.uiAddr + PAGE_SIZE;
OSInvalidateCPUCacheRangeKM(pvPageVAddr,
pvPageVAddr + PAGE_SIZE,
sCPUPhysAddrStart,
sCPUPhysAddrEnd);
}
#endif
psMemHandle->u.pvHandle = psPage;
sCpuPAddr.uiAddr = page_to_phys(psPage);
PhysHeapCpuPAddrToDevPAddr(psDevNode->apsPhysHeap[PVRSRV_DEVICE_PHYS_HEAP_CPU_LOCAL], psDevPAddr, &sCpuPAddr);
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#if !defined(PVRSRV_ENABLE_MEMORY_STATS)
PVRSRVStatsIncrMemAllocStat(PVRSRV_MEM_ALLOC_TYPE_ALLOC_PAGES_PT_UMA, PAGE_SIZE);
#else
PVRSRVStatsAddMemAllocRecord(PVRSRV_MEM_ALLOC_TYPE_ALLOC_PAGES_PT_UMA,
psPage,
sCpuPAddr,
PAGE_SIZE,
IMG_NULL);
#endif
#endif
return PVRSRV_OK;
}
void OSMMUPxFree(PVRSRV_DEVICE_NODE *psDevNode, Px_HANDLE *psMemHandle)
{
struct page *psPage = (struct page*) psMemHandle->u.pvHandle;
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#if !defined(PVRSRV_ENABLE_MEMORY_STATS)
PVRSRVStatsDecrMemAllocStat(PVRSRV_MEM_ALLOC_TYPE_ALLOC_PAGES_PT_UMA, PAGE_SIZE);
#else
PVRSRVStatsRemoveMemAllocRecord(PVRSRV_MEM_ALLOC_TYPE_ALLOC_PAGES_PT_UMA, (IMG_UINT64)(IMG_UINTPTR_T)psPage);
#endif
#endif
#if defined (CONFIG_X86)
{
IMG_PVOID pvPageVAddr;
int ret;
pvPageVAddr = page_address(psPage);
ret = set_memory_wb((unsigned long) pvPageVAddr, 1);
if (ret)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to reset page attribute", __FUNCTION__));
}
}
#endif
__free_page(psPage);
}
PVRSRV_ERROR OSMMUPxMap(PVRSRV_DEVICE_NODE *psDevNode, Px_HANDLE *psMemHandle,
IMG_SIZE_T uiSize, IMG_DEV_PHYADDR *psDevPAddr,
void **pvPtr)
{
struct page **ppsPage = (struct page **) &psMemHandle->u.pvHandle;
IMG_UINTPTR_T uiCPUVAddr;
pgprot_t prot = PAGE_KERNEL;
PVR_UNREFERENCED_PARAMETER(psDevNode);
prot = pgprot_writecombine(prot);
#if defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))
uiCPUVAddr = gen_pool_alloc(pvrsrv_pool_writecombine, PAGE_SIZE);
if (uiCPUVAddr) {
int ret = 0;
struct vm_struct tmp_area;
/* vmalloc and friends expect a guard page so we need to take that into account */
tmp_area.addr = (void *)uiCPUVAddr;
tmp_area.size = 2 * PAGE_SIZE;
ret = map_vm_area(&tmp_area, prot, &ppsPage);
if (ret) {
gen_pool_free(pvrsrv_pool_writecombine, uiCPUVAddr, PAGE_SIZE);
PVR_DPF((PVR_DBG_ERROR,
"%s: Cannot map page to pool",
__func__));
/* Failed the pool alloc so fall back to the vm_map path */
uiCPUVAddr = 0;
}
}
/* Not else as if the poll alloc fails it resets uiCPUVAddr to 0 */
if (uiCPUVAddr == 0)
#endif /* defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)) */
{
uiCPUVAddr = (IMG_UINTPTR_T) vm_map_ram(ppsPage,
1,
-1,
prot);
}
/* Check that one of the above methods got us an address */
if (((void *)uiCPUVAddr) == IMG_NULL)
{
return PVRSRV_ERROR_FAILED_TO_MAP_KERNELVIRTUAL;
}
*pvPtr = (void *) ((uiCPUVAddr & (~OSGetPageMask())) |
((IMG_UINTPTR_T) (psDevPAddr->uiAddr & OSGetPageMask())));
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#if !defined(PVRSRV_ENABLE_MEMORY_STATS)
/* Mapping is done a page at a time */
PVRSRVStatsIncrMemAllocStat(PVRSRV_MEM_ALLOC_TYPE_VMAP_PT_UMA, PAGE_SIZE);
#else
{
IMG_CPU_PHYADDR sCpuPAddr;
sCpuPAddr.uiAddr = 0;
PVRSRVStatsAddMemAllocRecord(PVRSRV_MEM_ALLOC_TYPE_VMAP_PT_UMA,
(void *)uiCPUVAddr,
sCpuPAddr,
uiSize,
IMG_NULL);
}
#endif
#endif
return PVRSRV_OK;
}
void OSMMUPxUnmap(PVRSRV_DEVICE_NODE *psDevNode, Px_HANDLE *psMemHandle, void *pvPtr)
{
PVR_UNREFERENCED_PARAMETER(psDevNode);
PVR_UNREFERENCED_PARAMETER(psMemHandle);
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#if !defined(PVRSRV_ENABLE_MEMORY_STATS)
/* Mapping is done a page at a time */
PVRSRVStatsDecrMemAllocStat(PVRSRV_MEM_ALLOC_TYPE_VMAP_PT_UMA, PAGE_SIZE);
#else
PVRSRVStatsRemoveMemAllocRecord(PVRSRV_MEM_ALLOC_TYPE_VMAP_PT_UMA, (IMG_UINT64)(IMG_UINTPTR_T)pvPtr);
#endif
#endif
#if defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))
if (vmap_from_pool(pvPtr))
{
unsigned long addr = (unsigned long)pvPtr;
/* Flush the data cache */
flush_cache_vunmap(addr, addr + PAGE_SIZE);
/* Unmap the page */
unmap_kernel_range_noflush(addr, PAGE_SIZE);
/* Flush the TLB */
__flush_tlb_single(addr);
/* Free the page back to the pool */
gen_pool_free(pvrsrv_pool_writecombine, addr, PAGE_SIZE);
}
else
#endif /* defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)) */
{
vm_unmap_ram(pvPtr, 1);
}
}
/*************************************************************************/ /*!
@Function OSMemCopy
@Description Copies memory around
@Input pvDst Pointer to dst
@Output pvSrc Pointer to src
@Input ui32Size Bytes to copy
*/ /**************************************************************************/
void OSMemCopy(void *pvDst, const void *pvSrc, IMG_SIZE_T ui32Size)
{
memcpy(pvDst, pvSrc, ui32Size);
}
/*************************************************************************/ /*!
@Function OSMemSet
@Description Function that does the same as the C memset() functions
@Modified *pvDest Pointer to start of buffer to be set
@Input ui8Value Value to set each byte to
@Input ui32Size Number of bytes to set
*/ /**************************************************************************/
void OSMemSet(void *pvDest, IMG_UINT8 ui8Value, IMG_SIZE_T ui32Size)
{
memset(pvDest, (char) ui8Value, (size_t) ui32Size);
}
IMG_INT OSMemCmp(void *pvBufA, void *pvBufB, IMG_SIZE_T uiLen)
{
return (IMG_INT) memcmp(pvBufA, pvBufB, uiLen);
}
/*************************************************************************/ /*!
@Function OSStringNCopy
@Description strcpy
*/ /**************************************************************************/
IMG_CHAR *OSStringNCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc, IMG_SIZE_T uSize)
{
return strncpy(pszDest, pszSrc, uSize);
}
/*************************************************************************/ /*!
@Function OSSNPrintf
@Description snprintf
@Return the chars written or -1 on error
*/ /**************************************************************************/
IMG_INT32 OSSNPrintf(IMG_CHAR *pStr, IMG_SIZE_T 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_SIZE_T OSStringLength(const IMG_CHAR *pStr)
{
return strlen(pStr);
}
IMG_SIZE_T OSStringNLength(const IMG_CHAR *pStr, IMG_SIZE_T uiCount)
{
return strnlen(pStr, uiCount);
}
IMG_INT32 OSStringCompare(const IMG_CHAR *pStr1, const IMG_CHAR *pStr2)
{
return strcmp(pStr1, pStr2);
}
/*************************************************************************/ /*!
@Function OSInitEnvData
@Description Allocates space for env specific data
@Input ppvEnvSpecificData Pointer to pointer in which to return
allocated data.
@Input ui32MMUMode MMU mode.
@Return PVRSRV_OK
*/ /**************************************************************************/
PVRSRV_ERROR OSInitEnvData(void)
{
/* allocate env specific data */
gpsEnvData = OSAllocMem(sizeof(ENV_DATA));
if (gpsEnvData == IMG_NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
gpsEnvData->pvBridgeData = OSAllocMem(PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE);
if (gpsEnvData->pvBridgeData == IMG_NULL)
{
OSFreeMem(gpsEnvData);
/*not nulling pointer, out of scope*/
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
#if defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))
/*
vm_ram_ram works with 2MB blocks to avoid excessive
TLB flushing but our allocations are always small and have
a long lifetime which then leads to fragmentation of vmalloc space.
To workaround this we create a virtual address pool in the vmap range
for mapping our page tables into so we don't fragment vmalloc space.
*/
if (!pvrsrv_pool_writecombine)
{
init_pvr_pool();
}
#endif /* defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)) */
return PVRSRV_OK;
}
/*************************************************************************/ /*!
@Function OSDeInitEnvData
@Description frees env specific data memory
@Input pvEnvSpecificData Pointer to private structure
@Return PVRSRV_OK on success else PVRSRV_ERROR_OUT_OF_MEMORY
*/ /**************************************************************************/
void OSDeInitEnvData(void)
{
ENV_DATA *psEnvData = gpsEnvData;
#if defined(CONFIG_GENERIC_ALLOCATOR) && defined(CONFIG_X86) && (LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0))
if (pvrsrv_pool_writecombine)
{
deinit_pvr_pool();
}
#endif
OSFreeMem(psEnvData->pvBridgeData);
psEnvData->pvBridgeData = IMG_NULL;
OSFreeMem(psEnvData);
}
ENV_DATA *OSGetEnvData(void)
{
return gpsEnvData;
}
/*************************************************************************/ /*!
@Function OSReleaseThreadQuanta
@Description Releases thread quanta
*/ /**************************************************************************/
void OSReleaseThreadQuanta(void)
{
schedule();
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
static inline IMG_UINT32 Clockus(void)
{
return (jiffies * (1000000 / HZ));
}
#else
/* Not matching/aligning this API to the Clockus() API above to avoid necessary
* multiplication/division operations in calling code.
*/
static inline IMG_UINT64 Clockns64(void)
{
IMG_UINT64 timenow;
/* Kernel thread preempt protection. Some architecture implementations
* (ARM) of sched_clock are not preempt safe when the kernel is configured
* as such e.g. CONFIG_PREEMPT and others.
*/
preempt_disable();
/* Using sched_clock instead of ktime_get since we need a time stamp that
* correlates with that shown in kernel logs and trace data not one that
* is a bit behind. */
timenow = sched_clock();
preempt_enable();
return timenow;
}
#endif
/*************************************************************************/ /*!
@Function OSClockns64
@Description
This function returns the clock in nanoseconds. Unlike OSClockus,
OSClockus64 has a near 64-bit range
*/ /**************************************************************************/
IMG_UINT64 OSClockns64(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
return Clockns64();
#else
return ((IMG_UINT64)Clockus()) * 1000ULL;
#endif
}
/*************************************************************************/ /*!
@Function OSClockus64
@Description
This function returns the clock in microseconds. Unlike OSClockus,
OSClockus64 has a near 64-bit range
*/ /**************************************************************************/
IMG_UINT64 OSClockus64(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
IMG_UINT64 timenow = Clockns64();
IMG_UINT32 remainder;
return OSDivide64r64(timenow, 1000, &remainder);
#else
return ((IMG_UINT64)Clockus());
#endif
}
/*************************************************************************/ /*!
@Function OSClockus
@Description This function returns the clock in microseconds
@Return clock (us)
*/ /**************************************************************************/
IMG_UINT32 OSClockus(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
return (IMG_UINT32) OSClockus64();
#else
return Clockus();
#endif
}
/*************************************************************************/ /*!
@Function OSClockms
@Description This function returns the clock in milliseconds
@Return clock (ms)
*/ /**************************************************************************/
IMG_UINT32 OSClockms(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
IMG_UINT64 timenow = Clockns64();
IMG_UINT32 remainder;
return OSDivide64(timenow, 1000000, &remainder);
#else
IMG_UINT64 time, j = (IMG_UINT32)jiffies;
time = j * (((1 << 16) * 1000) / HZ);
time >>= 16;
return (IMG_UINT32)time;
#endif
}
/*
OSWaitus
*/
void OSWaitus(IMG_UINT32 ui32Timeus)
{
udelay(ui32Timeus);
}
/*
OSSleepms
*/
void OSSleepms(IMG_UINT32 ui32Timems)
{
msleep(ui32Timems);
}
/*************************************************************************/ /*!
@Function OSGetCurrentProcessIDKM
@Description Returns ID of current process (thread group)
@Return ID of current process
*****************************************************************************/
IMG_PID OSGetCurrentProcessIDKM(void)
{
if (in_interrupt())
{
return KERNEL_ID;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
return (IMG_PID)current->pgrp;
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
return (IMG_PID)task_tgid_nr(current);
#else
return (IMG_PID)current->tgid;
#endif
#endif
}
/*************************************************************************/ /*!
@Function OSGetCurrentProcessNameKM
@Description gets name of current process
@Return process name
*****************************************************************************/
IMG_CHAR *OSGetCurrentProcessNameKM(void)
{
return current->comm;
}
/*************************************************************************/ /*!
@Function OSGetCurrentThreadIDKM
@Description Returns ID for current thread
@Return ID of current thread
*****************************************************************************/
IMG_PID OSGetCurrentThreadIDKM(IMG_VOID)
{
if (in_interrupt())
{
return KERNEL_ID;
}
return current->pid;
}
/*************************************************************************/ /*!
@Function OSGetPageSize
@Description gets page size
@Return page size
*/ /**************************************************************************/
IMG_SIZE_T OSGetPageSize(void)
{
return PAGE_SIZE;
}
/*************************************************************************/ /*!
@Function OSGetPageShift
@Description gets page size
@Return page size
*/ /**************************************************************************/
IMG_SIZE_T OSGetPageShift(void)
{
return PAGE_SHIFT;
}
/*************************************************************************/ /*!
@Function OSGetPageMask
@Description gets page mask
@Return page size
*/ /**************************************************************************/
IMG_SIZE_T OSGetPageMask(void)
{
return (OSGetPageSize()-1);
}
#if !defined (SUPPORT_SYSTEM_INTERRUPT_HANDLING)
typedef struct _LISR_DATA_ {
PFN_LISR pfnLISR;
void *pvData;
IMG_UINT32 ui32IRQ;
} LISR_DATA;
/*
DeviceISRWrapper
*/
static irqreturn_t DeviceISRWrapper(int irq, void *dev_id)
{
LISR_DATA *psLISRData = (LISR_DATA *) dev_id;
IMG_BOOL bStatus = IMG_FALSE;
PVR_UNREFERENCED_PARAMETER(irq);
bStatus = psLISRData->pfnLISR(psLISRData->pvData);
return bStatus ? IRQ_HANDLED : IRQ_NONE;
}
#endif
/*
OSInstallDeviceLISR
*/
PVRSRV_ERROR OSInstallDeviceLISR(PVRSRV_DEVICE_CONFIG *psDevConfig,
IMG_HANDLE *hLISRData,
PFN_LISR pfnLISR,
void *pvData)
{
#if defined(SUPPORT_SYSTEM_INTERRUPT_HANDLING)
return SysInstallDeviceLISR(psDevConfig->ui32IRQ,
psDevConfig->bIRQIsShared,
psDevConfig->pszName,
pfnLISR,
pvData,
hLISRData);
#else
LISR_DATA *psLISRData;
unsigned long flags = 0;
psLISRData = kmalloc(sizeof(LISR_DATA), GFP_KERNEL);
if (psDevConfig->bIRQIsShared)
{
flags = IRQF_SHARED;
}
psLISRData->pfnLISR = pfnLISR;
psLISRData->pvData = pvData;
psLISRData->ui32IRQ = psDevConfig->ui32IRQ;
PVR_TRACE(("Installing device LISR %s on IRQ %d with cookie %p", psDevConfig->pszName, psDevConfig->ui32IRQ, pvData));
if(request_irq(psDevConfig->ui32IRQ, DeviceISRWrapper,
flags, psDevConfig->pszName, psLISRData))
{
PVR_DPF((PVR_DBG_ERROR,"OSInstallDeviceLISR: Couldn't install device LISR on IRQ %d", psDevConfig->ui32IRQ));
return PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR;
}
*hLISRData = (IMG_HANDLE) psLISRData;
return PVRSRV_OK;
#endif
}
/*
OSUninstallDeviceLISR
*/
PVRSRV_ERROR OSUninstallDeviceLISR(IMG_HANDLE hLISRData)
{
#if defined (SUPPORT_SYSTEM_INTERRUPT_HANDLING)
return SysUninstallDeviceLISR(hLISRData);
#else
LISR_DATA *psLISRData = (LISR_DATA *) hLISRData;
PVR_TRACE(("Uninstalling device LISR on IRQ %d with cookie %p", psLISRData->ui32IRQ, psLISRData->pvData));
free_irq(psLISRData->ui32IRQ, psLISRData);
kfree(psLISRData);
return PVRSRV_OK;
#endif
}
#if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE)
typedef struct _MISR_DATA_ {
struct workqueue_struct *psWorkQueue;
struct work_struct sMISRWork;
PFN_MISR pfnMISR;
void *hData;
} MISR_DATA;
/*
MISRWrapper
*/
static void MISRWrapper(struct work_struct *data)
{
MISR_DATA *psMISRData = container_of(data, MISR_DATA, sMISRWork);
psMISRData->pfnMISR(psMISRData->hData);
}
/*
OSInstallMISR
*/
PVRSRV_ERROR OSInstallMISR(IMG_HANDLE *hMISRData, PFN_MISR pfnMISR,
void *hData)
{
MISR_DATA *psMISRData;
psMISRData = kmalloc(sizeof(MISR_DATA), GFP_KERNEL);
if (psMISRData == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
psMISRData->hData = hData;
psMISRData->pfnMISR = pfnMISR;
PVR_TRACE(("Installing MISR with cookie %p", psMISRData));
psMISRData->psWorkQueue = create_singlethread_workqueue("pvr_workqueue");
if (psMISRData->psWorkQueue == IMG_NULL)
{
PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: create_singlethreaded_workqueue failed"));
kfree(psMISRData);
return PVRSRV_ERROR_UNABLE_TO_CREATE_THREAD;
}
INIT_WORK(&psMISRData->sMISRWork, MISRWrapper);
*hMISRData = (IMG_HANDLE) psMISRData;
return PVRSRV_OK;
}
/*
OSUninstallMISR
*/
PVRSRV_ERROR OSUninstallMISR(IMG_HANDLE hMISRData)
{
MISR_DATA *psMISRData = (MISR_DATA *) hMISRData;
PVR_TRACE(("Uninstalling MISR"));
destroy_workqueue(psMISRData->psWorkQueue);
kfree(psMISRData);
return PVRSRV_OK;
}
/*
OSScheduleMISR
*/
PVRSRV_ERROR OSScheduleMISR(IMG_HANDLE hMISRData)
{
MISR_DATA *psMISRData = (MISR_DATA *) hMISRData;
/*
Note:
In the case of NO_HARDWARE we want the driver to be synchronous so
that we don't have to worry about waiting for previous operations
to complete
*/
#if defined(NO_HARDWARE)
psMISRData->pfnMISR(psMISRData->hData);
#else
queue_work(psMISRData->psWorkQueue, &psMISRData->sMISRWork);
#endif
return PVRSRV_OK;
}
#else /* defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) */
#if defined(PVR_LINUX_MISR_USING_WORKQUEUE)
typedef struct _MISR_DATA_ {
struct work_struct sMISRWork;
PFN_MISR pfnMISR;
void *hData;
} MISR_DATA;
/*
MISRWrapper
*/
static void MISRWrapper(struct work_struct *data)
{
MISR_DATA *psMISRData = container_of(data, MISR_DATA, sMISRWork);
psMISRData->pfnMISR(psMISRData->hData);
}
/*
OSInstallMISR
*/
PVRSRV_ERROR OSInstallMISR(IMG_HANDLE *hMISRData, PFN_MISR pfnMISR, void *hData)
{
MISR_DATA *psMISRData;
psMISRData = kmalloc(sizeof(MISR_DATA), GFP_KERNEL);
if (psMISRData == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
psMISRData->hData = hData;
psMISRData->pfnMISR = pfnMISR;
PVR_TRACE(("Installing MISR with cookie %p", psMISRData));
INIT_WORK(&psMISRData->sMISRWork, MISRWrapper);
*hMISRData = (IMG_HANDLE) psMISRData;
return PVRSRV_OK;
}
/*
OSUninstallMISR
*/
PVRSRV_ERROR OSUninstallMISR(IMG_HANDLE hMISRData)
{
PVR_TRACE(("Uninstalling MISR"));
flush_scheduled_work();
kfree(hMISRData);
return PVRSRV_OK;
}
/*
OSScheduleMISR
*/
PVRSRV_ERROR OSScheduleMISR(IMG_HANDLE hMISRData)
{
MISR_DATA *psMISRData = hMISRData;
#if defined(NO_HARDWARE)
psMISRData->pfnMISR(psMISRData->hData);
#else
schedule_work(&psMISRData->sMISRWork);
#endif
return PVRSRV_OK;
}
#else /* #if defined(PVR_LINUX_MISR_USING_WORKQUEUE) */
typedef struct _MISR_DATA_ {
struct tasklet_struct sMISRTasklet;
PFN_MISR pfnMISR;
void *hData;
} MISR_DATA;
/*
MISRWrapper
*/
static void MISRWrapper(unsigned long data)
{
MISR_DATA *psMISRData = (MISR_DATA *) data;
psMISRData->pfnMISR(psMISRData->hData);
}
/*
OSInstallMISR
*/
PVRSRV_ERROR OSInstallMISR(IMG_HANDLE *hMISRData, PFN_MISR pfnMISR, void *hData)
{
MISR_DATA *psMISRData;
psMISRData = kmalloc(sizeof(MISR_DATA), GFP_KERNEL);
if (psMISRData == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
psMISRData->hData = hData;
psMISRData->pfnMISR = pfnMISR;
PVR_TRACE(("Installing MISR with cookie %p", psMISRData));
tasklet_init(&psMISRData->sMISRTasklet, MISRWrapper, (unsigned long)psMISRData);
*hMISRData = (IMG_HANDLE) psMISRData;
return PVRSRV_OK;
}
/*
OSUninstallMISR
*/
PVRSRV_ERROR OSUninstallMISR(IMG_HANDLE hMISRData)
{
MISR_DATA *psMISRData = (MISR_DATA *) hMISRData;
PVR_TRACE(("Uninstalling MISR"));
tasklet_kill(&psMISRData->sMISRTasklet);
return PVRSRV_OK;
}
/*
OSScheduleMISR
*/
PVRSRV_ERROR OSScheduleMISR(IMG_HANDLE hMISRData)
{
MISR_DATA *psMISRData = (MISR_DATA *) hMISRData;
#if defined(NO_HARDWARE)
psMISRData->pfnMISR(psMISRData->hData);
#else
tasklet_schedule(&psMISRData->sMISRTasklet);
#endif
return PVRSRV_OK;
}
#endif /* #if defined(PVR_LINUX_MISR_USING_WORKQUEUE) */
#endif /* #if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) */
/* OS specific values for thread priority */
const IMG_INT32 ai32OSPriorityValues[LAST_PRIORITY] = { -20, /* HIGHEST_PRIORITY */
-10, /* HIGH_PRIRIOTY */
0, /* NORMAL_PRIORITY */
9, /* LOW_PRIORITY */
19, /* LOWEST_PRIORITY */
-22};/* NOSET_PRIORITY */
typedef struct {
struct task_struct *kthread;
PFN_THREAD pfnThread;
void *hData;
OS_THREAD_LEVEL eThreadPriority;
} OSThreadData;
static int OSThreadRun(void *data)
{
OSThreadData *psOSThreadData = data;
/* If i32NiceValue is acceptable, set the nice value for the new thread */
if (psOSThreadData->eThreadPriority != NOSET_PRIORITY &&
psOSThreadData->eThreadPriority < LAST_PRIORITY)
set_user_nice(current, ai32OSPriorityValues[psOSThreadData->eThreadPriority]);
/* Call the client's kernel thread with the client's data pointer */
psOSThreadData->pfnThread(psOSThreadData->hData);
/* Wait for OSThreadDestroy() to call kthread_stop() */
while (!kthread_should_stop())
{
schedule();
}
return 0;
}
PVRSRV_ERROR OSThreadCreate(IMG_HANDLE *phThread,
IMG_CHAR *pszThreadName,
PFN_THREAD pfnThread,
void *hData)
{
/* Call OSThreadCreatePriority with an invalid nice value */
return OSThreadCreatePriority(phThread, pszThreadName, pfnThread, hData, NOSET_PRIORITY);
}
PVRSRV_ERROR OSThreadCreatePriority(IMG_HANDLE *phThread,
IMG_CHAR *pszThreadName,
PFN_THREAD pfnThread,
void *hData,
OS_THREAD_LEVEL eThreadPriority)
{
OSThreadData *psOSThreadData;
PVRSRV_ERROR eError;
psOSThreadData = kmalloc(sizeof(OSThreadData), GFP_KERNEL);
if (psOSThreadData == IMG_NULL)
{
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
goto fail_alloc;
}
psOSThreadData->pfnThread = pfnThread;
psOSThreadData->hData = hData;
psOSThreadData->eThreadPriority= eThreadPriority;
psOSThreadData->kthread = kthread_run(OSThreadRun, psOSThreadData, pszThreadName);
if (IS_ERR(psOSThreadData->kthread))
{
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
goto fail_kthread;
}
*phThread = psOSThreadData;
return PVRSRV_OK;
fail_kthread:
kfree(psOSThreadData);
fail_alloc:
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
PVRSRV_ERROR OSThreadDestroy(IMG_HANDLE hThread)
{
OSThreadData *psOSThreadData = hThread;
int ret;
/* Let the thread know we are ready for it to end and wait for it. */
ret = kthread_stop(psOSThreadData->kthread);
PVR_ASSERT(ret == 0);
kfree(psOSThreadData);
return PVRSRV_OK;
}
void OSPanic(void)
{
BUG();
#if defined(__KLOCWORK__)
/* Klocworks does not understand that BUG is terminal... */
abort();
#endif
}
/*************************************************************************/ /*!
@Function OSMapPhysToLin
@Description Maps the physical memory into linear addr range
@Input BasePAddr Physical cpu address
@Input ui32Bytes Bytes to map
@Input ui32CacheType Cache type
@Return Linear addr of mapping on success, else NULL
*/ /**************************************************************************/
void *
OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr,
IMG_SIZE_T ui32Bytes,
IMG_UINT32 ui32MappingFlags)
{
void *pvIORemapCookie;
pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags);
if(pvIORemapCookie == IMG_NULL)
{
PVR_ASSERT(0);
return IMG_NULL;
}
return pvIORemapCookie;
}
/*************************************************************************/ /*!
@Function OSUnMapPhysToLin
@Description Unmaps memory that was mapped with OSMapPhysToLin
@Input pvLinAddr
@Input ui32Bytes
@Return TRUE on success, else FALSE
*/ /**************************************************************************/
IMG_BOOL
OSUnMapPhysToLin(void *pvLinAddr, IMG_SIZE_T ui32Bytes, IMG_UINT32 ui32MappingFlags)
{
PVR_UNREFERENCED_PARAMETER(ui32Bytes);
IOUnmapWrapper(pvLinAddr);
return IMG_TRUE;
}
/*
OSReadHWReg8
*/
IMG_UINT8 OSReadHWReg8(IMG_PVOID pvLinRegBaseAddr,
IMG_UINT32 ui32Offset)
{
#if !defined(NO_HARDWARE)
return (IMG_UINT8) readb((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#else
return 0x4e;
#endif
}
/*
OSReadHWReg16
*/
IMG_UINT16 OSReadHWReg16(IMG_PVOID pvLinRegBaseAddr,
IMG_UINT32 ui32Offset)
{
#if !defined(NO_HARDWARE)
return (IMG_UINT16) readw((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#else
return 0x3a4e;
#endif
}
/*
OSReadHWReg32
*/
IMG_UINT32 OSReadHWReg32(IMG_PVOID pvLinRegBaseAddr,
IMG_UINT32 ui32Offset)
{
#if !defined(NO_HARDWARE)
return (IMG_UINT32) readl((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#else
return 0x30f73a4e;
#endif
}
/*
OSReadHWReg64
*/
IMG_UINT64 OSReadHWReg64(IMG_PVOID pvLinRegBaseAddr,
IMG_UINT32 ui32Offset)
{
IMG_UINT64 ui64Result;
ui64Result = OSReadHWReg32(pvLinRegBaseAddr, ui32Offset + 4);
ui64Result <<= 32;
ui64Result |= (IMG_UINT64)OSReadHWReg32(pvLinRegBaseAddr, ui32Offset);
return ui64Result;
}
/*
OSReadHWRegBank
*/
IMG_DEVMEM_SIZE_T OSReadHWRegBank(IMG_PVOID pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT8 *pui8DstBuf,
IMG_DEVMEM_SIZE_T uiDstBufLen)
{
#if !defined(NO_HARDWARE)
IMG_DEVMEM_SIZE_T uiCounter;
for(uiCounter = 0; uiCounter < uiDstBufLen; uiCounter++) {
*(pui8DstBuf + uiCounter) =
readb(pvLinRegBaseAddr + ui32Offset + uiCounter);
}
return uiCounter;
#else
return uiDstBufLen;
#endif
}
/*
OSWriteHWReg8
*/
void OSWriteHWReg8(void *pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT8 ui8Value)
{
#if !defined(NO_HARDWARE)
writeb(ui8Value, (IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#endif
}
/*
OSWriteHWReg16
*/
void OSWriteHWReg16(void *pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT16 ui16Value)
{
#if !defined(NO_HARDWARE)
writew(ui16Value, (IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#endif
}
/*
OSWriteHWReg32
*/
void OSWriteHWReg32(void *pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT32 ui32Value)
{
#if !defined(NO_HARDWARE)
writel(ui32Value, (IMG_PBYTE)pvLinRegBaseAddr+ui32Offset);
#endif
}
/*
OSWriteHWReg64
*/
void OSWriteHWReg64(void *pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT64 ui64Value)
{
#if !defined(NO_HARDWARE)
IMG_UINT32 ui32ValueLow, ui32ValueHigh;
ui32ValueLow = ui64Value & 0xffffffff;
ui32ValueHigh = ((IMG_UINT64) (ui64Value >> 32)) & 0xffffffff;
writel(ui32ValueLow, pvLinRegBaseAddr + ui32Offset);
writel(ui32ValueHigh, pvLinRegBaseAddr + ui32Offset + 4);
#endif
}
IMG_DEVMEM_SIZE_T OSWriteHWRegBank(void *pvLinRegBaseAddr,
IMG_UINT32 ui32Offset,
IMG_UINT8 *pui8SrcBuf,
IMG_DEVMEM_SIZE_T uiSrcBufLen)
{
#if !defined(NO_HARDWARE)
IMG_DEVMEM_SIZE_T uiCounter;
for(uiCounter = 0; uiCounter < uiSrcBufLen; uiCounter++) {
writeb(*(pui8SrcBuf + uiCounter),
pvLinRegBaseAddr + ui32Offset + uiCounter);
}
return uiCounter;
#else
return uiSrcBufLen;
#endif
}
#define OS_MAX_TIMERS 8
/* Timer callback strucure used by OSAddTimer */
typedef struct TIMER_CALLBACK_DATA_TAG
{
IMG_BOOL bInUse;
PFN_TIMER_FUNC pfnTimerFunc;
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))
/* The lock is used to control access to sTimers */
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;
/* call timer callback */
psTimerCBData->pfnTimerFunc(psTimerCBData->pvData);
/* reset timer */
mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies);
}
/*************************************************************************/ /*!
@Function OSTimerCallbackWrapper
@Description OS specific timer callback wrapper function
@Input uData Timer callback data
*/ /**************************************************************************/
static void OSTimerCallbackWrapper(IMG_UINTPTR_T uData)
{
TIMER_CALLBACK_DATA *psTimerCBData = (TIMER_CALLBACK_DATA*)uData;
#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
/*************************************************************************/ /*!
@Function OSAddTimer
@Description OS specific function to install a timer callback
@Input pfnTimerFunc Timer callback
@Input *pvData Callback data
@Input ui32MsTimeout Callback period
@Return Valid handle success, NULL failure
*/ /**************************************************************************/
IMG_HANDLE OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, 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
/* check callback */
if(!pfnTimerFunc)
{
PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: passed invalid callback"));
return IMG_NULL;
}
/* Allocate timer callback data structure */
#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;
/*
HZ = ticks per second
ui32MsTimeout = required ms delay
ticks = (Hz * ui32MsTimeout) / 1000
*/
psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000)
? 1
: ((HZ * ui32MsTimeout) / 1000);
/* initialise object */
init_timer(&psTimerCBData->sTimer);
/* setup timer object */
psTimerCBData->sTimer.function = (void *)OSTimerCallbackWrapper;
psTimerCBData->sTimer.data = (IMG_UINTPTR_T)psTimerCBData;
return (IMG_HANDLE)(IMG_UINTPTR_T)(ui32i + 1);
}
static inline TIMER_CALLBACK_DATA *GetTimerStructure(IMG_HANDLE hTimer)
{
IMG_UINT32 ui32i = (IMG_UINT32)((IMG_UINTPTR_T)hTimer) - 1;
PVR_ASSERT(ui32i < OS_MAX_TIMERS);
return &sTimers[ui32i];
}
/*************************************************************************/ /*!
@Function OSRemoveTimer
@Description OS specific function to remove a timer callback
@Input hTimer : timer handle
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSRemoveTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(!psTimerCBData->bActive);
/* free timer callback data struct */
psTimerCBData->bInUse = IMG_FALSE;
return PVRSRV_OK;
}
/*************************************************************************/ /*!
@Function OSEnableTimer
@Description OS specific function to enable a timer callback
@Input hTimer Timer handle
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSEnableTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(!psTimerCBData->bActive);
/* Start timer arming */
psTimerCBData->bActive = IMG_TRUE;
/* set the expire time */
psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies;
/* Add the timer to the list */
add_timer(&psTimerCBData->sTimer);
return PVRSRV_OK;
}
/*************************************************************************/ /*!
@Function OSDisableTimer
@Description OS specific function to disable a timer callback
@Input hTimer Timer handle
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer)
{
TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
PVR_ASSERT(psTimerCBData->bInUse);
PVR_ASSERT(psTimerCBData->bActive);
/* Stop timer from arming */
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
/* remove timer */
del_timer_sync(&psTimerCBData->sTimer);
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
/*
* This second flush is to catch the case where the timer ran
* before we managed to delete it, in which case, it will have
* queued more work for the workqueue. Since the bActive flag
* has been cleared, this second flush won't result in the
* timer being rearmed.
*/
flush_workqueue(psTimerWorkQueue);
#endif
#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)
flush_scheduled_work();
#endif
return PVRSRV_OK;
}
/*************************************************************************/ /*!
@Function OSEventObjectCreate
@Description OS specific function to create an event object
@Input pszName Globally unique event object name (if null name must be autogenerated)
@Output hEventObject OS event object info structure
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectCreate(const IMG_CHAR *pszName, IMG_HANDLE *hEventObject)
{
PVRSRV_ERROR eError = PVRSRV_OK;
PVR_UNREFERENCED_PARAMETER(pszName);
if(hEventObject)
{
if(LinuxEventObjectListCreate(hEventObject) != PVRSRV_OK)
{
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectCreate: hEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_UNABLE_TO_CREATE_EVENT;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSEventObjectDestroy
@Description OS specific function to destroy an event object
@Input hEventObject OS event object info structure
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectDestroy(IMG_HANDLE hEventObject)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(hEventObject)
{
LinuxEventObjectListDestroy(hEventObject);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroy: hEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSEventObjectWaitTimeout
@Description Wait for an event with timeout as supplied. Called from client
@Input hOSEventKM OS and kernel specific handle to event object
@Input uiTimeoutMs Non zero time period in milliseconds to wait
@Return PVRSRV_ERROR_TIMEOUT : Wait reached wait limit and timed out
@Return PVRSRV_ERROR : any other system error code
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectWaitTimeout(IMG_HANDLE hOSEventKM, IMG_UINT32 uiTimeoutMs)
{
PVRSRV_ERROR eError;
if(hOSEventKM && uiTimeoutMs > 0)
{
eError = LinuxEventObjectWait(hOSEventKM, uiTimeoutMs);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectWait: invalid arguments %p, %d", hOSEventKM, uiTimeoutMs ));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSEventObjectWait
@Description OS specific function to wait for an event object. Called
from client. Uses a default wait with 100ms timeout.
@Input hOSEventKM OS and kernel specific handle to event object
@Return PVRSRV_ERROR_TIMEOUT : Reached wait limit and timed out
@Return PVRSRV_ERROR : any other system error code
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectWait(IMG_HANDLE hOSEventKM)
{
return OSEventObjectWaitTimeout(hOSEventKM, EVENT_OBJECT_TIMEOUT_MS);
}
/*************************************************************************/ /*!
@Function OSEventObjectOpen
@Description OS specific function to open an event object. Called from client
@Input hEventObject Pointer to an event object
@Output phOSEvent OS and kernel specific handle to event object
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectOpen(IMG_HANDLE hEventObject,
IMG_HANDLE *phOSEvent)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(hEventObject)
{
if(LinuxEventObjectAdd(hEventObject, phOSEvent) != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: failed"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectOpen: hEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSEventObjectClose
@Description OS specific function to close an event object. Called from client
@Input hOSEventKM OS and kernel specific handle to event object
@Return PVRSRV_ERROR :
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectClose(IMG_HANDLE hOSEventKM)
{
PVRSRV_ERROR eError = PVRSRV_OK;
if(hOSEventKM)
{
if(LinuxEventObjectDelete(hOSEventKM) != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectDelete: failed"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroy: hEventObject is not a valid pointer"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSEventObjectSignal
@Description OS specific function to 'signal' an event object. Called from L/MISR
@Input hOSEventKM OS and kernel specific handle to event object
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR OSEventObjectSignal(IMG_HANDLE hEventObject)
{
PVRSRV_ERROR eError;
if(hEventObject)
{
eError = LinuxEventObjectSignal(hEventObject);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectSignal: hOSEventKM is not a valid handle"));
eError = PVRSRV_ERROR_INVALID_PARAMS;
}
return eError;
}
/*************************************************************************/ /*!
@Function OSProcHasPrivSrvInit
@Description Does the process have sufficient privileges to initialise services?
@Return IMG_BOOL
*/ /**************************************************************************/
IMG_BOOL OSProcHasPrivSrvInit(void)
{
return capable(CAP_SYS_ADMIN) != 0;
}
/*************************************************************************/ /*!
@Function OSCopyToUser
@Description Copy a block of data into user space
@Input pvSrc
@Output pvDest
@Input ui32Bytes
@Return PVRSRV_ERROR :
*/ /**************************************************************************/
PVRSRV_ERROR OSCopyToUser(IMG_PVOID pvProcess,
void *pvDest,
const void *pvSrc,
IMG_SIZE_T 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;
}
/*************************************************************************/ /*!
@Function OSCopyFromUser
@Description Copy a block of data from the user space
@Output pvDest
@Input pvSrc
@Input ui32Bytes
@Return PVRSRV_ERROR :
*/ /**************************************************************************/
PVRSRV_ERROR OSCopyFromUser(IMG_PVOID pvProcess,
void *pvDest,
const void *pvSrc,
IMG_SIZE_T 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;
}
/*************************************************************************/ /*!
@Function OSAccessOK
@Description Checks if a user space pointer is valide
@Input eVerification
@Input pvUserPtr
@Input ui32Bytes
@Return IMG_BOOL :
*/ /**************************************************************************/
IMG_BOOL OSAccessOK(IMG_VERIFY_TEST eVerification, void *pvUserPtr, IMG_SIZE_T 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);
}
void OSWriteMemoryBarrier(void)
{
wmb();
}
void OSMemoryBarrier(void)
{
mb();
}
PVRSRV_ERROR OSWRLockCreate(POSWR_LOCK *ppsLock)
{
POSWR_LOCK psLock;
psLock = kmalloc(sizeof(*psLock), GFP_KERNEL);
if (psLock == NULL)
{
return PVRSRV_ERROR_OUT_OF_MEMORY;
}
init_rwsem(&psLock->sRWLock);
*ppsLock = psLock;
return PVRSRV_OK;
}
void OSWRLockDestroy(POSWR_LOCK psLock)
{
kfree(psLock);
}
IMG_UINT64 OSDivide64r64(IMG_UINT64 ui64Divident, IMG_UINT32 ui32Divisor, IMG_UINT32 *pui32Remainder)
{
*pui32Remainder = do_div(ui64Divident, ui32Divisor);
return ui64Divident;
}
IMG_UINT32 OSDivide64(IMG_UINT64 ui64Divident, IMG_UINT32 ui32Divisor, IMG_UINT32 *pui32Remainder)
{
*pui32Remainder = do_div(ui64Divident, ui32Divisor);
return (IMG_UINT32) ui64Divident;
}
/* One time osfunc initialisation */
PVRSRV_ERROR PVROSFuncInit(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;
}
/*
* Osfunc deinitialisation.
* Note that PVROSFuncInit may not have been called
*/
void PVROSFuncDeInit(void)
{
#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES)
if (psTimerWorkQueue != NULL)
{
destroy_workqueue(psTimerWorkQueue);
}
#endif
}
static IMG_BOOL gbDoRelease = IMG_TRUE;
void OSSetReleasePVRLock(void){ gbDoRelease = IMG_TRUE; }
void OSSetKeepPVRLock(void) { gbDoRelease = IMG_FALSE;}
IMG_BOOL OSGetReleasePVRLock(void){ return gbDoRelease;}
void OSDumpStack(void)
{
dump_stack();
}
void OSAcquireBridgeLock(void)
{
mutex_lock(&gPVRSRVLock);
}
void OSReleaseBridgeLock(void)
{
mutex_unlock(&gPVRSRVLock);
}
IMG_BOOL OSIsBridgeLockedByMe()
{
return (mutex_is_locked(&gPVRSRVLock) && current == gPVRSRVLock.owner);
}
/*************************************************************************/ /*!
@Function OSCreateStatisticEntry
@Description Create a statistic entry in the specified folder.
@Input pszName String containing the name for the entry.
@Input pvFolder Reference from OSCreateStatisticFolder() of the
folder to create the entry in, or IMG_NULL for the
root.
@Input pfnGetElement Pointer to function that can be used to obtain the
value of the statistic.
@Input pvData OS specific reference that can be used by
pfnGetElement.
@Return Pointer void reference to the entry created, which can be
passed to OSRemoveStatisticEntry() to remove the entry.
*/ /**************************************************************************/
IMG_PVOID OSCreateStatisticEntry(IMG_CHAR* pszName, IMG_PVOID pvFolder,
OS_GET_STATS_ELEMENT_FUNC* pfnGetElement,
IMG_PVOID pvData)
{
return PVRDebugFSCreateStatisticEntry(pszName, pvFolder, pfnGetElement, pvData);
} /* OSCreateStatisticEntry */
/*************************************************************************/ /*!
@Function OSRemoveStatisticEntry
@Description Removes a statistic entry.
@Input pvEntry Pointer void reference to the entry created by
OSCreateStatisticEntry().
*/ /**************************************************************************/
void OSRemoveStatisticEntry(IMG_PVOID pvEntry)
{
PVRDebugFSRemoveStatisticEntry(pvEntry);
} /* OSRemoveStatisticEntry */
/*************************************************************************/ /*!
@Function OSCreateStatisticFolder
@Description Create a statistic folder to hold statistic entries.
@Input pszName String containing the name for the folder.
@Input pvFolder Reference from OSCreateStatisticFolder() of the folder
to create the folder in, or IMG_NULL for the root.
@Return Pointer void reference to the folder created, which can be
passed to OSRemoveStatisticFolder() to remove the folder.
*/ /**************************************************************************/
IMG_PVOID OSCreateStatisticFolder(IMG_CHAR *pszName, IMG_PVOID pvFolder)
{
struct dentry *psDir;
int iResult;
iResult = PVRDebugFSCreateEntryDir(pszName, pvFolder, &psDir);
return (iResult == 0) ? psDir : IMG_NULL;
} /* OSCreateStatisticFolder */
/*************************************************************************/ /*!
@Function OSRemoveStatisticFolder
@Description Removes a statistic folder.
@Input pvFolder Reference from OSCreateStatisticFolder() of the
folder that should be removed.
*/ /**************************************************************************/
void OSRemoveStatisticFolder(IMG_PVOID pvFolder)
{
PVRDebugFSRemoveEntryDir((struct dentry *)pvFolder);
} /* OSRemoveStatisticFolder */