blob: be90433fdfa6f648cbb3829625133daaa92c232b [file] [log] [blame]
/**********************************************************************
*
* Copyright(c) 2008 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>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/hardirq.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <plat/omap-pm.h>
#include "sgxdefs.h"
#include "services_headers.h"
#include "sysinfo.h"
#include "sgxapi_km.h"
#include "sysconfig.h"
#include "sgxinfokm.h"
#include "syslocal.h"
#if !defined(PVR_LINUX_USING_WORKQUEUES)
#error "PVR_LINUX_USING_WORKQUEUES must be defined"
#endif
#define ONE_MHZ 1000000
#define HZ_TO_MHZ(m) ((m) / ONE_MHZ)
#if defined(SUPPORT_OMAP3430_SGXFCLK_96M)
#define SGX_PARENT_CLOCK "cm_96m_fck"
#else
#define SGX_PARENT_CLOCK "core_ck"
#endif
extern struct platform_device *gpsPVRLDMDev;
#if defined(SGX530) && (SGX_CORE_REV == 125)
#define OMAP_MEMORY_BUS_CLOCK_MAX 800000
#else
#define OMAP_MEMORY_BUS_CLOCK_MAX 664000
#endif
static IMG_VOID PowerLockWrap(SYS_SPECIFIC_DATA *psSysSpecData)
{
if (!in_interrupt())
{
BUG_ON(in_atomic());
mutex_lock(&psSysSpecData->sPowerLock);
}
}
static IMG_VOID PowerLockUnwrap(SYS_SPECIFIC_DATA *psSysSpecData)
{
if (!in_interrupt())
{
BUG_ON(in_atomic());
mutex_unlock(&psSysSpecData->sPowerLock);
}
}
PVRSRV_ERROR SysPowerLockWrap(SYS_DATA *psSysData)
{
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
PowerLockWrap(psSysSpecData);
return PVRSRV_OK;
}
IMG_VOID SysPowerLockUnwrap(SYS_DATA *psSysData)
{
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
PowerLockUnwrap(psSysSpecData);
}
IMG_BOOL WrapSystemPowerChange(SYS_SPECIFIC_DATA *psSysSpecData)
{
return IMG_TRUE;
}
IMG_VOID UnwrapSystemPowerChange(SYS_SPECIFIC_DATA *psSysSpecData)
{
}
static inline IMG_UINT32 scale_by_rate(IMG_UINT32 val, IMG_UINT32 rate1, IMG_UINT32 rate2)
{
if (rate1 >= rate2)
{
return val * (rate1 / rate2);
}
return val / (rate2 / rate1);
}
static inline IMG_UINT32 scale_prop_to_SGX_clock(IMG_UINT32 val, IMG_UINT32 rate)
{
return scale_by_rate(val, rate, SYS_SGX_CLOCK_SPEED);
}
static inline IMG_UINT32 scale_inv_prop_to_SGX_clock(IMG_UINT32 val, IMG_UINT32 rate)
{
return scale_by_rate(val, SYS_SGX_CLOCK_SPEED, rate);
}
IMG_VOID SysGetSGXTimingInformation(SGX_TIMING_INFORMATION *psTimingInfo)
{
IMG_UINT32 rate;
#if defined(NO_HARDWARE)
rate = SYS_SGX_CLOCK_SPEED;
#else
PVR_ASSERT(atomic_read(&gpsSysSpecificData->sSGXClocksEnabled) != 0);
rate = clk_get_rate(gpsSysSpecificData->psSGX_FCK);
PVR_ASSERT(rate != 0);
#endif
psTimingInfo->ui32CoreClockSpeed = rate;
psTimingInfo->ui32HWRecoveryFreq = scale_prop_to_SGX_clock(SYS_SGX_HWRECOVERY_TIMEOUT_FREQ, rate);
psTimingInfo->ui32uKernelFreq = scale_prop_to_SGX_clock(SYS_SGX_PDS_TIMER_FREQ, rate);
#if defined(SUPPORT_ACTIVE_POWER_MANAGEMENT)
psTimingInfo->bEnableActivePM = IMG_TRUE;
#else
psTimingInfo->bEnableActivePM = IMG_FALSE;
#endif
psTimingInfo->ui32ActivePowManLatencyms = SYS_SGX_ACTIVE_POWER_LATENCY_MS;
}
PVRSRV_ERROR EnableSGXClocks(SYS_DATA *psSysData)
{
#if !defined(NO_HARDWARE)
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
long lNewRate;
long lRate;
IMG_INT res;
if (atomic_read(&psSysSpecData->sSGXClocksEnabled) != 0)
{
return PVRSRV_OK;
}
PVR_DPF((PVR_DBG_MESSAGE, "EnableSGXClocks: Enabling SGX Clocks"));
#if defined(DEBUG)
{
IMG_UINT32 rate = clk_get_rate(psSysSpecData->psMPU_CK);
PVR_DPF((PVR_DBG_MESSAGE, "EnableSGXClocks: CPU Clock is %dMhz", HZ_TO_MHZ(rate)));
}
#endif
res = clk_enable(psSysSpecData->psSGX_FCK);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSGXClocks: Couldn't enable SGX functional clock (%d)", res));
return PVRSRV_ERROR_UNABLE_TO_ENABLE_CLOCK;
}
res = clk_enable(psSysSpecData->psSGX_ICK);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSGXClocks: Couldn't enable SGX interface clock (%d)", res));
clk_disable(psSysSpecData->psSGX_FCK);
return PVRSRV_ERROR_UNABLE_TO_ENABLE_CLOCK;
}
lNewRate = clk_round_rate(psSysSpecData->psSGX_FCK, SYS_SGX_CLOCK_SPEED + ONE_MHZ);
if (lNewRate <= 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSGXClocks: Couldn't round SGX functional clock rate"));
return PVRSRV_ERROR_UNABLE_TO_ROUND_CLOCK_RATE;
}
lRate = clk_get_rate(psSysSpecData->psSGX_FCK);
if (lRate != lNewRate)
{
res = clk_set_rate(psSysSpecData->psSGX_FCK, lNewRate);
if (res < 0)
{
PVR_DPF((PVR_DBG_WARNING, "EnableSGXClocks: Couldn't set SGX functional clock rate (%d)", res));
}
}
#if defined(DEBUG)
{
IMG_UINT32 rate = clk_get_rate(psSysSpecData->psSGX_FCK);
PVR_DPF((PVR_DBG_MESSAGE, "EnableSGXClocks: SGX Functional Clock is %dMhz", HZ_TO_MHZ(rate)));
}
#endif
#if defined(SYS_OMAP3430_PIN_MEMORY_BUS_CLOCK)
omap_pm_set_min_bus_tput(&gpsPVRLDMDev->dev, OCP_INITIATOR_AGENT, OMAP_MEMORY_BUS_CLOCK_MAX);
#endif
atomic_set(&psSysSpecData->sSGXClocksEnabled, 1);
#else /* !defined(NO_HARDWARE) */
PVR_UNREFERENCED_PARAMETER(psSysData);
#endif /* !defined(NO_HARDWARE) */
return PVRSRV_OK;
}
IMG_VOID DisableSGXClocks(SYS_DATA *psSysData)
{
#if !defined(NO_HARDWARE)
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
if (atomic_read(&psSysSpecData->sSGXClocksEnabled) == 0)
{
return;
}
PVR_DPF((PVR_DBG_MESSAGE, "DisableSGXClocks: Disabling SGX Clocks"));
if (psSysSpecData->psSGX_ICK)
{
clk_disable(psSysSpecData->psSGX_ICK);
}
if (psSysSpecData->psSGX_FCK)
{
clk_disable(psSysSpecData->psSGX_FCK);
}
#if defined(SYS_OMAP3430_PIN_MEMORY_BUS_CLOCK)
omap_pm_set_min_bus_tput(&gpsPVRLDMDev->dev, OCP_INITIATOR_AGENT, 0);
#endif
atomic_set(&psSysSpecData->sSGXClocksEnabled, 0);
#else
PVR_UNREFERENCED_PARAMETER(psSysData);
#endif
}
PVRSRV_ERROR EnableSystemClocks(SYS_DATA *psSysData)
{
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
struct clk *psCLK;
IMG_INT res;
PVRSRV_ERROR eError;
#if defined(DEBUG) || defined(TIMING)
IMG_INT rate;
struct clk *sys_ck;
IMG_CPU_PHYADDR TimerRegPhysBase;
IMG_HANDLE hTimerEnable;
IMG_UINT32 *pui32TimerEnable;
#endif
PVR_TRACE(("EnableSystemClocks: Enabling System Clocks"));
if (!psSysSpecData->bSysClocksOneTimeInit)
{
mutex_init(&psSysSpecData->sPowerLock);
atomic_set(&psSysSpecData->sSGXClocksEnabled, 0);
psCLK = clk_get(NULL, SGX_PARENT_CLOCK);
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSsystemClocks: Couldn't get Core Clock"));
goto ExitError;
}
psSysSpecData->psCORE_CK = psCLK;
psCLK = clk_get(NULL, "sgx_fck");
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSsystemClocks: Couldn't get SGX Functional Clock"));
goto ExitError;
}
psSysSpecData->psSGX_FCK = psCLK;
psCLK = clk_get(NULL, "sgx_ick");
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't get SGX Interface Clock"));
goto ExitError;
}
psSysSpecData->psSGX_ICK = psCLK;
#if defined(DEBUG)
psCLK = clk_get(NULL, "mpu_ck");
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't get MPU Clock"));
goto ExitError;
}
psSysSpecData->psMPU_CK = psCLK;
#endif
res = clk_set_parent(psSysSpecData->psSGX_FCK, psSysSpecData->psCORE_CK);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't set SGX parent clock (%d)", res));
goto ExitError;
}
psSysSpecData->bSysClocksOneTimeInit = IMG_TRUE;
}
#if defined(DEBUG) || defined(TIMING)
psCLK = clk_get(NULL, "gpt11_fck");
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't get GPTIMER11 functional clock"));
goto ExitUnRegisterConstraintNotifications;
}
psSysSpecData->psGPT11_FCK = psCLK;
psCLK = clk_get(NULL, "gpt11_ick");
if (IS_ERR(psCLK))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't get GPTIMER11 interface clock"));
goto ExitUnRegisterConstraintNotifications;
}
psSysSpecData->psGPT11_ICK = psCLK;
sys_ck = clk_get(NULL, "sys_ck");
if (IS_ERR(sys_ck))
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't get System clock"));
goto ExitUnRegisterConstraintNotifications;
}
if(clk_get_parent(psSysSpecData->psGPT11_FCK) != sys_ck)
{
PVR_TRACE(("Setting GPTIMER11 parent to System Clock"));
res = clk_set_parent(psSysSpecData->psGPT11_FCK, sys_ck);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't set GPTIMER11 parent clock (%d)", res));
goto ExitUnRegisterConstraintNotifications;
}
}
rate = clk_get_rate(psSysSpecData->psGPT11_FCK);
PVR_TRACE(("GPTIMER11 clock is %dMHz", HZ_TO_MHZ(rate)));
res = clk_enable(psSysSpecData->psGPT11_FCK);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't enable GPTIMER11 functional clock (%d)", res));
goto ExitUnRegisterConstraintNotifications;
}
res = clk_enable(psSysSpecData->psGPT11_ICK);
if (res < 0)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: Couldn't enable GPTIMER11 interface clock (%d)", res));
goto ExitDisableGPT11FCK;
}
TimerRegPhysBase.uiAddr = SYS_OMAP3430_GP11TIMER_TSICR_SYS_PHYS_BASE;
pui32TimerEnable = OSMapPhysToLin(TimerRegPhysBase,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
&hTimerEnable);
if (pui32TimerEnable == IMG_NULL)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: OSMapPhysToLin failed"));
goto ExitDisableGPT11ICK;
}
rate = *pui32TimerEnable;
if(!(rate & 4))
{
PVR_TRACE(("Setting GPTIMER11 mode to posted (currently is non-posted)"));
*pui32TimerEnable = rate | 4;
}
OSUnMapPhysToLin(pui32TimerEnable,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
hTimerEnable);
TimerRegPhysBase.uiAddr = SYS_OMAP3430_GP11TIMER_ENABLE_SYS_PHYS_BASE;
pui32TimerEnable = OSMapPhysToLin(TimerRegPhysBase,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
&hTimerEnable);
if (pui32TimerEnable == IMG_NULL)
{
PVR_DPF((PVR_DBG_ERROR, "EnableSystemClocks: OSMapPhysToLin failed"));
goto ExitDisableGPT11ICK;
}
*pui32TimerEnable = 3;
OSUnMapPhysToLin(pui32TimerEnable,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
hTimerEnable);
#endif
eError = PVRSRV_OK;
goto Exit;
#if defined(DEBUG) || defined(TIMING)
ExitDisableGPT11ICK:
clk_disable(psSysSpecData->psGPT11_ICK);
ExitDisableGPT11FCK:
clk_disable(psSysSpecData->psGPT11_FCK);
ExitUnRegisterConstraintNotifications:
#endif
ExitError:
eError = PVRSRV_ERROR_DISABLE_CLOCK_FAILURE;
Exit:
return eError;
}
IMG_VOID DisableSystemClocks(SYS_DATA *psSysData)
{
#if defined(DEBUG) || defined(TIMING)
SYS_SPECIFIC_DATA *psSysSpecData = (SYS_SPECIFIC_DATA *) psSysData->pvSysSpecificData;
IMG_CPU_PHYADDR TimerRegPhysBase;
IMG_HANDLE hTimerDisable;
IMG_UINT32 *pui32TimerDisable;
#endif
PVR_TRACE(("DisableSystemClocks: Disabling System Clocks"));
DisableSGXClocks(psSysData);
#if defined(DEBUG) || defined(TIMING)
TimerRegPhysBase.uiAddr = SYS_OMAP3430_GP11TIMER_ENABLE_SYS_PHYS_BASE;
pui32TimerDisable = OSMapPhysToLin(TimerRegPhysBase,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
&hTimerDisable);
if (pui32TimerDisable == IMG_NULL)
{
PVR_DPF((PVR_DBG_ERROR, "DisableSystemClocks: OSMapPhysToLin failed"));
}
else
{
*pui32TimerDisable = 0;
OSUnMapPhysToLin(pui32TimerDisable,
4,
PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED,
hTimerDisable);
}
clk_disable(psSysSpecData->psGPT11_ICK);
clk_disable(psSysSpecData->psGPT11_FCK);
#endif
}