2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2012-11-11 22:50:48 +01:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2012-11-01 16:19:01 +01:00
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2012-11-10 10:15:11 +01:00
# include <map>
2013-08-26 01:04:37 -07:00
# include <vector>
2020-08-10 00:12:51 -07:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
2013-08-21 01:10:30 -07:00
# include "Core/HLE/HLE.h"
2014-03-15 11:22:19 -07:00
# include "Core/HLE/FunctionWrappers.h"
2013-08-21 01:10:30 -07:00
# include "Core/CoreTiming.h"
2014-03-15 11:22:19 -07:00
# include "Core/MemMap.h"
2013-08-21 01:10:30 -07:00
# include "Core/Reporting.h"
2013-06-12 17:15:45 -04:00
# include "Core/Config.h"
2012-11-01 16:19:01 +01:00
2013-08-21 01:10:30 -07:00
# include "Core/HLE/scePower.h"
# include "Core/HLE/sceKernelThread.h"
2013-09-09 23:49:06 -07:00
# include "Core/HLE/sceKernelInterrupt.h"
2013-08-21 01:10:30 -07:00
2013-08-26 01:04:37 -07:00
struct VolatileWaitingThread {
SceUID threadID ;
u32 addrPtr ;
u32 sizePtr ;
} ;
2012-12-09 13:48:25 -08:00
const int PSP_POWER_ERROR_TAKEN_SLOT = 0x80000020 ;
const int PSP_POWER_ERROR_SLOTS_FULL = 0x80000022 ;
const int PSP_POWER_ERROR_EMPTY_SLOT = 0x80000025 ;
const int PSP_POWER_ERROR_INVALID_CB = 0x80000100 ;
const int PSP_POWER_ERROR_INVALID_SLOT = 0x80000102 ;
const int PSP_POWER_CB_AC_POWER = 0x00001000 ;
const int PSP_POWER_CB_BATTERY_EXIST = 0x00000080 ;
const int PSP_POWER_CB_BATTERY_FULL = 0x00000064 ;
2012-11-11 21:32:44 +00:00
const int POWER_CB_AUTO = - 1 ;
2012-12-09 13:48:25 -08:00
2014-04-03 21:38:58 +02:00
// These are the callback slots for user mode applications.
2012-11-11 21:32:44 +00:00
const int numberOfCBPowerSlots = 16 ;
2014-04-03 18:12:22 +02:00
2014-04-03 21:38:58 +02:00
// These are the callback slots for kernel mode applications.
2012-12-09 13:48:25 -08:00
const int numberOfCBPowerSlotsPrivate = 32 ;
2012-11-11 21:32:44 +00:00
2012-12-23 22:47:52 -08:00
static bool volatileMemLocked ;
static int powerCbSlots [ numberOfCBPowerSlots ] ;
2013-08-26 01:04:37 -07:00
static std : : vector < VolatileWaitingThread > volatileWaitingThreads ;
2012-11-10 10:15:11 +01:00
2013-11-28 12:35:15 -05:00
// Should this belong here, or in CoreTiming?
2019-04-14 14:51:35 -07:00
static int RealpllFreq = 222000000 ;
static int RealbusFreq = 111000000 ;
static int pllFreq = 222000000 ;
static int busFreq = 111000000 ;
// The CPU mhz can only be a multiple of the PLL divided by 511.
int PowerCpuMhzToHz ( int desired , int pllHz ) {
double maxfreq = desired * 1000000.0 ;
double step = ( double ) pllHz / 511.0 ;
// These values seem to be locked.
if ( pllHz > = 333000000 & & desired = = 333 ) {
return 333000000 ;
} else if ( pllHz > = 222000000 & & desired = = 222 ) {
return 222000000 ;
}
double freq = 0 ;
while ( freq + step < maxfreq ) {
freq + = step ;
}
// We match the PSP's HLE funcs better when we have the same float error, it seems.
2022-01-30 22:06:54 -08:00
return ( int ) ( ( float ) ( freq / 1000000.0f ) * 1000000 ) ;
2019-04-14 14:51:35 -07:00
}
int PowerPllMhzToHz ( int mhz ) {
// These seem to be the only steps it has.
if ( mhz < = 190 )
return 190285721 ;
if ( mhz < = 222 )
return 222000000 ;
if ( mhz < = 266 )
return 266399994 ;
if ( mhz < = 333 )
return 333000000 ;
return mhz * 1000000 ;
}
int PowerBusMhzToHz ( int mhz ) {
// These seem to be the only steps it has.
if ( mhz < = 95 )
return 95142860 ;
if ( mhz < = 111 )
return 111000000 ;
if ( mhz < = 133 )
return 133199997 ;
if ( mhz < = 166 )
return 166500000 ;
return mhz * 1000000 ;
}
2013-03-02 14:32:31 +01:00
2012-11-10 10:15:11 +01:00
void __PowerInit ( ) {
memset ( powerCbSlots , 0 , sizeof ( powerCbSlots ) ) ;
2012-12-23 22:47:52 -08:00
volatileMemLocked = false ;
2013-08-26 01:04:37 -07:00
volatileWaitingThreads . clear ( ) ;
2013-06-12 17:15:45 -04:00
2014-03-04 07:55:08 -08:00
if ( g_Config . iLockedCPUSpeed > 0 ) {
2019-04-14 14:51:35 -07:00
pllFreq = PowerPllMhzToHz ( g_Config . iLockedCPUSpeed ) ;
busFreq = PowerBusMhzToHz ( pllFreq / 2000000 ) ;
CoreTiming : : SetClockFrequencyHz ( PowerCpuMhzToHz ( g_Config . iLockedCPUSpeed , pllFreq ) ) ;
2014-03-04 07:55:08 -08:00
} else {
2019-04-14 14:51:35 -07:00
pllFreq = PowerPllMhzToHz ( 222 ) ;
busFreq = PowerBusMhzToHz ( 111 ) ;
2013-06-12 17:15:45 -04:00
}
2019-04-14 14:51:35 -07:00
RealpllFreq = PowerPllMhzToHz ( 222 ) ;
RealbusFreq = PowerBusMhzToHz ( 111 ) ;
2012-11-10 10:15:11 +01:00
}
2012-12-27 23:00:04 -08:00
void __PowerDoState ( PointerWrap & p ) {
2016-12-30 07:01:37 +08:00
auto s = p . Section ( " scePower " , 1 , 2 ) ;
2013-09-14 20:23:03 -07:00
if ( ! s )
return ;
2016-12-30 07:01:37 +08:00
if ( s > = 2 ) {
2020-08-09 21:20:42 -07:00
Do ( p , RealpllFreq ) ;
Do ( p , RealbusFreq ) ;
2019-04-14 14:51:35 -07:00
if ( RealpllFreq < 1000000 )
RealpllFreq = PowerPllMhzToHz ( RealpllFreq ) ;
if ( RealbusFreq < 1000000 )
RealbusFreq = PowerBusMhzToHz ( RealbusFreq ) ;
} else {
RealpllFreq = PowerPllMhzToHz ( 222 ) ;
RealbusFreq = PowerBusMhzToHz ( 111 ) ;
2016-12-30 07:01:37 +08:00
}
if ( g_Config . iLockedCPUSpeed > 0 ) {
2019-04-14 14:51:35 -07:00
pllFreq = PowerPllMhzToHz ( g_Config . iLockedCPUSpeed ) ;
busFreq = PowerBusMhzToHz ( pllFreq / 2000000 ) ;
CoreTiming : : SetClockFrequencyHz ( PowerCpuMhzToHz ( g_Config . iLockedCPUSpeed , pllFreq ) ) ;
} else {
2016-12-30 07:01:37 +08:00
pllFreq = RealpllFreq ;
busFreq = RealbusFreq ;
}
2020-08-09 21:20:42 -07:00
DoArray ( p , powerCbSlots , ARRAY_SIZE ( powerCbSlots ) ) ;
Do ( p , volatileMemLocked ) ;
Do ( p , volatileWaitingThreads ) ;
2012-12-27 23:00:04 -08:00
}
2014-12-08 04:40:08 -05:00
static int scePowerGetBatteryLifePercent ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 100=scePowerGetBatteryLifePercent " ) ;
2012-11-11 21:32:44 +00:00
return 100 ;
2012-11-01 16:19:01 +01:00
}
2012-11-11 22:50:48 +01:00
2014-12-08 04:40:08 -05:00
static int scePowerGetBatteryLifeTime ( ) {
2013-01-11 00:15:16 -08:00
DEBUG_LOG ( HLE , " 0=scePowerGetBatteryLifeTime() " ) ;
// 0 means we're on AC power.
return 0 ;
}
2014-12-08 04:40:08 -05:00
static int scePowerGetBatteryTemp ( ) {
2013-06-23 16:52:41 +09:00
DEBUG_LOG ( HLE , " 0=scePowerGetBatteryTemp() " ) ;
// 0 means celsius temperature of the battery
return 0 ;
}
2014-12-08 04:40:08 -05:00
static int scePowerIsPowerOnline ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 1=scePowerIsPowerOnline " ) ;
2012-11-11 21:32:44 +00:00
return 1 ;
2012-11-01 16:19:01 +01:00
}
2012-11-11 22:50:48 +01:00
2014-12-08 04:40:08 -05:00
static int scePowerIsBatteryExist ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 1=scePowerIsBatteryExist " ) ;
2012-11-11 21:32:44 +00:00
return 1 ;
2012-11-01 16:19:01 +01:00
}
2012-11-11 22:50:48 +01:00
2014-12-08 04:40:08 -05:00
static int scePowerIsBatteryCharging ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 0=scePowerIsBatteryCharging " ) ;
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2012-11-11 22:50:48 +01:00
2014-12-08 04:40:08 -05:00
static int scePowerGetBatteryChargingStatus ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 0=scePowerGetBatteryChargingStatus " ) ;
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2012-11-11 22:50:48 +01:00
2014-12-08 04:40:08 -05:00
static int scePowerIsLowBattery ( ) {
2012-11-01 16:19:01 +01:00
DEBUG_LOG ( HLE , " 0=scePowerIsLowBattery " ) ;
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static int scePowerRegisterCallback ( int slot , int cbId ) {
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " 0=scePowerRegisterCallback(%i, %i) " , slot , cbId ) ;
if ( slot < - 1 | | slot > = numberOfCBPowerSlotsPrivate ) {
return PSP_POWER_ERROR_INVALID_SLOT ;
}
if ( slot > = numberOfCBPowerSlots ) {
2014-04-03 18:12:22 +02:00
return SCE_KERNEL_ERROR_PRIV_REQUIRED ;
2012-12-09 13:48:25 -08:00
}
// TODO: If cbId is invalid return PSP_POWER_ERROR_INVALID_CB.
if ( cbId = = 0 ) {
return PSP_POWER_ERROR_INVALID_CB ;
}
int retval = - 1 ;
2012-11-11 21:32:44 +00:00
2012-12-09 19:41:19 +07:00
if ( slot = = POWER_CB_AUTO ) { // -1 signifies auto select of bank
for ( int i = 0 ; i < numberOfCBPowerSlots ; i + + ) {
2012-12-09 13:48:25 -08:00
if ( powerCbSlots [ i ] = = 0 & & retval = = - 1 ) { // found an empty slot
2012-11-11 21:32:44 +00:00
powerCbSlots [ i ] = cbId ;
2012-12-09 13:48:25 -08:00
retval = i ;
2012-11-11 19:30:48 +01:00
}
}
2012-12-09 13:48:25 -08:00
if ( retval = = - 1 ) {
return PSP_POWER_ERROR_SLOTS_FULL ;
}
2012-12-09 19:41:19 +07:00
} else {
if ( powerCbSlots [ slot ] = = 0 ) {
2012-11-11 21:32:44 +00:00
powerCbSlots [ slot ] = cbId ;
2012-12-09 13:48:25 -08:00
retval = 0 ;
2012-12-09 19:41:19 +07:00
} else {
2012-12-09 13:48:25 -08:00
return PSP_POWER_ERROR_TAKEN_SLOT ;
2012-11-11 19:30:48 +01:00
}
}
2012-12-09 13:48:25 -08:00
if ( retval > = 0 ) {
int arg = PSP_POWER_CB_AC_POWER | PSP_POWER_CB_BATTERY_EXIST | PSP_POWER_CB_BATTERY_FULL ;
2013-09-02 01:25:41 -07:00
__KernelNotifyCallback ( cbId , arg ) ;
2012-11-11 21:32:44 +00:00
}
2012-12-09 13:48:25 -08:00
return retval ;
2012-11-10 10:15:11 +01:00
}
2014-12-08 04:40:08 -05:00
static int scePowerUnregisterCallback ( int slotId ) {
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " 0=scePowerUnregisterCallback(%i) " , slotId ) ;
if ( slotId < 0 | | slotId > = numberOfCBPowerSlotsPrivate ) {
return PSP_POWER_ERROR_INVALID_SLOT ;
}
if ( slotId > = numberOfCBPowerSlots ) {
2014-04-03 18:42:10 +02:00
return SCE_KERNEL_ERROR_PRIV_REQUIRED ;
2012-11-11 22:50:48 +01:00
}
2012-11-11 21:32:44 +00:00
2012-12-09 19:41:19 +07:00
if ( powerCbSlots [ slotId ] ! = 0 ) {
2012-11-11 19:30:48 +01:00
int cbId = powerCbSlots [ slotId ] ;
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " 0=scePowerUnregisterCallback(%i) (cbid = %i) " , slotId , cbId ) ;
2012-11-11 19:30:48 +01:00
powerCbSlots [ slotId ] = 0 ;
2012-12-09 19:41:19 +07:00
} else {
2012-12-09 13:48:25 -08:00
return PSP_POWER_ERROR_EMPTY_SLOT ;
2012-11-11 22:50:48 +01:00
}
2012-11-11 21:32:44 +00:00
2012-11-11 22:50:48 +01:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2012-11-10 10:15:11 +01:00
2014-12-08 04:40:08 -05:00
static int sceKernelPowerLock ( int lockType ) {
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " 0=sceKernelPowerLock(%i) " , lockType ) ;
if ( lockType = = 0 ) {
return 0 ;
} else {
2013-03-31 23:42:56 -07:00
return SCE_KERNEL_ERROR_INVALID_MODE ;
2012-12-09 13:48:25 -08:00
}
2012-11-01 16:19:01 +01:00
}
2012-12-09 19:41:19 +07:00
2014-12-08 04:40:08 -05:00
static int sceKernelPowerUnlock ( int lockType ) {
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " 0=sceKernelPowerUnlock(%i) " , lockType ) ;
if ( lockType = = 0 ) {
return 0 ;
} else {
2013-03-31 23:42:56 -07:00
return SCE_KERNEL_ERROR_INVALID_MODE ;
2012-12-09 13:48:25 -08:00
}
2012-11-01 16:19:01 +01:00
}
2012-12-09 19:41:19 +07:00
2014-12-08 04:40:08 -05:00
static int sceKernelPowerTick ( int flag ) {
2012-12-09 13:48:25 -08:00
DEBUG_LOG ( HLE , " UNIMPL 0=sceKernelPowerTick(%i) " , flag ) ;
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2021-01-18 10:37:12 -08:00
int KernelVolatileMemLock ( int type , u32 paddr , u32 psize ) {
2013-08-26 01:04:37 -07:00
if ( type ! = 0 ) {
return SCE_KERNEL_ERROR_INVALID_MODE ;
}
if ( volatileMemLocked ) {
2014-04-27 20:31:40 +08:00
return SCE_KERNEL_ERROR_POWER_VMEM_IN_USE ;
2012-11-11 21:32:44 +00:00
}
2012-11-01 16:19:01 +01:00
2012-11-11 21:32:44 +00:00
// Volatile RAM is always at 0x08400000 and is of size 0x00400000.
// It's always available in the emu.
2012-11-19 09:25:50 +01:00
// TODO: Should really reserve this properly!
2014-03-24 00:49:27 -07:00
if ( Memory : : IsValidAddress ( paddr ) ) {
Memory : : Write_U32 ( 0x08400000 , paddr ) ;
}
if ( Memory : : IsValidAddress ( psize ) ) {
Memory : : Write_U32 ( 0x00400000 , psize ) ;
}
2013-08-26 01:04:37 -07:00
volatileMemLocked = true ;
2012-11-01 16:19:01 +01:00
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static int sceKernelVolatileMemTryLock ( int type , u32 paddr , u32 psize ) {
2021-01-18 10:37:12 -08:00
u32 error = KernelVolatileMemLock ( type , paddr , psize ) ;
2013-08-26 01:04:37 -07:00
switch ( error ) {
case 0 :
2014-03-24 00:49:27 -07:00
// HACK: This fixes Crash Tag Team Racing.
// Should only wait 1200 cycles though according to Unknown's testing,
// and with that it's still broken. So it's not this, unfortunately.
// Leaving it in for the 0.9.8 release anyway.
hleEatCycles ( 500000 ) ;
2013-11-08 18:52:07 +01:00
DEBUG_LOG ( HLE , " sceKernelVolatileMemTryLock(%i, %08x, %08x) - success " , type , paddr , psize ) ;
2013-08-26 01:04:37 -07:00
break ;
2014-04-27 20:31:40 +08:00
case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE :
2013-08-26 01:04:37 -07:00
ERROR_LOG ( HLE , " sceKernelVolatileMemTryLock(%i, %08x, %08x) - already locked! " , type , paddr , psize ) ;
break ;
default :
ERROR_LOG_REPORT ( HLE , " %08x=sceKernelVolatileMemTryLock(%i, %08x, %08x) - error " , type , paddr , psize , error ) ;
break ;
}
return error ;
}
2021-01-18 10:37:12 -08:00
int KernelVolatileMemUnlock ( int type ) {
2013-08-26 01:04:37 -07:00
if ( type ! = 0 ) {
return SCE_KERNEL_ERROR_INVALID_MODE ;
}
2021-01-18 10:37:12 -08:00
if ( ! volatileMemLocked ) {
2013-09-09 23:49:06 -07:00
// I guess it must use a sema.
return SCE_KERNEL_ERROR_SEMA_OVF ;
2012-12-09 19:41:19 +07:00
}
2021-01-18 10:37:12 -08:00
volatileMemLocked = false ;
// Wake someone, always fifo.
bool wokeThreads = false ;
u32 error ;
while ( ! volatileWaitingThreads . empty ( ) & & ! volatileMemLocked ) {
VolatileWaitingThread waitInfo = volatileWaitingThreads . front ( ) ;
volatileWaitingThreads . erase ( volatileWaitingThreads . begin ( ) ) ;
int waitID = __KernelGetWaitID ( waitInfo . threadID , WAITTYPE_VMEM , error ) ;
// If they were force-released, just skip.
if ( waitID = = 1 & & KernelVolatileMemLock ( 0 , waitInfo . addrPtr , waitInfo . sizePtr ) = = 0 ) {
__KernelResumeThreadFromWait ( waitInfo . threadID , 0 ) ;
wokeThreads = true ;
}
}
if ( wokeThreads ) {
INFO_LOG ( HLE , " KernelVolatileMemUnlock(%i) handed over to another thread " , type ) ;
hleReSchedule ( " volatile mem unlocked " ) ;
}
2012-11-11 21:32:44 +00:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2021-01-18 10:37:12 -08:00
static int sceKernelVolatileMemUnlock ( int type ) {
int error = KernelVolatileMemUnlock ( type ) ;
if ( error = = SCE_KERNEL_ERROR_INVALID_MODE ) {
ERROR_LOG_REPORT ( HLE , " sceKernelVolatileMemUnlock(%i) - invalid mode " , type ) ;
return error ;
} else if ( error = = SCE_KERNEL_ERROR_SEMA_OVF ) {
ERROR_LOG_REPORT ( HLE , " sceKernelVolatileMemUnlock(%i) FAILED - not locked " , type ) ;
return error ;
}
return hleLogSuccessI ( HLE , 0 ) ;
}
2014-12-08 04:40:08 -05:00
static int sceKernelVolatileMemLock ( int type , u32 paddr , u32 psize ) {
2013-09-09 23:49:06 -07:00
u32 error = 0 ;
// If dispatch is disabled or in an interrupt, don't check, just return an error.
// But still write the addr and size (some games require this to work, and it's testably true.)
if ( ! __KernelIsDispatchEnabled ( ) ) {
error = SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
} else if ( __IsInInterrupt ( ) ) {
error = SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
} else {
2021-01-18 10:37:12 -08:00
error = KernelVolatileMemLock ( type , paddr , psize ) ;
2013-09-09 23:49:06 -07:00
}
2013-08-26 01:04:37 -07:00
switch ( error ) {
case 0 :
2014-03-24 00:49:27 -07:00
// Should only wait 1200 cycles though according to Unknown's testing,
2017-02-03 01:13:02 +01:00
hleEatCycles ( 1200 ) ;
2013-11-08 18:52:07 +01:00
DEBUG_LOG ( HLE , " sceKernelVolatileMemLock(%i, %08x, %08x) - success " , type , paddr , psize ) ;
2013-08-26 01:04:37 -07:00
break ;
2014-04-27 20:31:40 +08:00
case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE :
2013-08-26 01:04:37 -07:00
{
WARN_LOG ( HLE , " sceKernelVolatileMemLock(%i, %08x, %08x) - already locked, waiting " , type , paddr , psize ) ;
const VolatileWaitingThread waitInfo = { __KernelGetCurThread ( ) , paddr , psize } ;
volatileWaitingThreads . push_back ( waitInfo ) ;
__KernelWaitCurThread ( WAITTYPE_VMEM , 1 , 0 , 0 , false , " volatile mem waited " ) ;
}
break ;
2013-09-09 23:49:06 -07:00
case SCE_KERNEL_ERROR_CAN_NOT_WAIT :
{
WARN_LOG ( HLE , " sceKernelVolatileMemLock(%i, %08x, %08x): dispatch disabled " , type , paddr , psize ) ;
Memory : : Write_U32 ( 0x08400000 , paddr ) ;
Memory : : Write_U32 ( 0x00400000 , psize ) ;
}
break ;
case SCE_KERNEL_ERROR_ILLEGAL_CONTEXT :
{
2013-09-10 01:21:20 -07:00
WARN_LOG ( HLE , " sceKernelVolatileMemLock(%i, %08x, %08x): in interrupt " , type , paddr , psize ) ;
2013-09-09 23:49:06 -07:00
Memory : : Write_U32 ( 0x08400000 , paddr ) ;
Memory : : Write_U32 ( 0x00400000 , psize ) ;
}
break ;
2013-08-26 01:04:37 -07:00
default :
ERROR_LOG_REPORT ( HLE , " %08x=sceKernelVolatileMemLock(%i, %08x, %08x) - error " , type , paddr , psize , error ) ;
break ;
}
return error ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerSetClockFrequency ( u32 pllfreq , u32 cpufreq , u32 busfreq ) {
2019-04-14 14:51:35 -07:00
// 190 might (probably) be a typo for 19, but it's what the actual PSP validates against.
if ( pllfreq < 19 | | pllfreq < cpufreq | | pllfreq > 333 ) {
return hleLogWarning ( SCEMISC , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid pll frequency " ) ;
}
2016-12-30 07:01:37 +08:00
if ( cpufreq = = 0 | | cpufreq > 333 ) {
2019-04-14 14:51:35 -07:00
return hleLogWarning ( SCEMISC , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid cpu frequency " ) ;
}
if ( busfreq = = 0 | | busfreq > 166 ) {
return hleLogWarning ( SCEMISC , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid bus frequency " ) ;
2016-12-30 07:01:37 +08:00
}
// TODO: More restrictions.
2013-11-08 18:52:07 +01:00
if ( g_Config . iLockedCPUSpeed > 0 ) {
2016-12-30 07:01:37 +08:00
INFO_LOG ( HLE , " scePowerSetClockFrequency(%i,%i,%i): locked by user config at %i, %i, %i " , pllfreq , cpufreq , busfreq , g_Config . iLockedCPUSpeed , g_Config . iLockedCPUSpeed , busFreq ) ;
2019-04-14 14:51:35 -07:00
} else {
2016-12-30 07:01:37 +08:00
INFO_LOG ( HLE , " scePowerSetClockFrequency(%i,%i,%i) " , pllfreq , cpufreq , busfreq ) ;
}
2019-04-14 14:51:35 -07:00
// Only reschedules when the stepped PLL frequency changes.
// It seems like the busfreq parameter has no effect (but can cause errors.)
if ( RealpllFreq ! = PowerPllMhzToHz ( pllfreq ) ) {
2019-04-14 15:06:32 -07:00
int oldPll = RealpllFreq / 1000000 ;
2019-04-14 14:51:35 -07:00
RealpllFreq = PowerPllMhzToHz ( pllfreq ) ;
RealbusFreq = PowerBusMhzToHz ( RealpllFreq / 2000000 ) ;
2016-12-30 07:01:37 +08:00
if ( g_Config . iLockedCPUSpeed < = 0 ) {
2019-04-14 14:51:35 -07:00
pllFreq = RealpllFreq ;
busFreq = RealbusFreq ;
CoreTiming : : SetClockFrequencyHz ( PowerCpuMhzToHz ( cpufreq , pllFreq ) ) ;
2013-03-05 08:21:21 -08:00
}
2019-04-14 15:06:32 -07:00
// The delay depends on the source and destination frequency, most are 150ms.
int newPll = RealpllFreq / 1000000 ;
int usec = 150000 ;
if ( ( newPll = = 190 & & oldPll = = 222 ) | | ( newPll = = 222 & & oldPll = = 190 ) )
usec = 15700 ;
else if ( ( newPll = = 266 & & oldPll = = 333 ) | | ( newPll = = 333 & & oldPll = = 266 ) )
usec = 16600 ;
return hleDelayResult ( 0 , " scepower set clockFrequency " , usec ) ;
2013-06-12 17:15:45 -04:00
}
2016-12-30 07:01:37 +08:00
if ( g_Config . iLockedCPUSpeed < = 0 )
2019-04-14 14:51:35 -07:00
CoreTiming : : SetClockFrequencyHz ( PowerCpuMhzToHz ( cpufreq , pllFreq ) ) ;
2016-12-30 07:01:37 +08:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerSetCpuClockFrequency ( u32 cpufreq ) {
2019-04-14 14:51:35 -07:00
if ( cpufreq = = 0 | | cpufreq > 333 ) {
return hleLogWarning ( SCEMISC , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid frequency " ) ;
2013-06-12 17:15:45 -04:00
}
2019-04-14 14:51:35 -07:00
if ( g_Config . iLockedCPUSpeed > 0 ) {
return hleLogDebug ( SCEMISC , 0 , " locked by user config at %i " , g_Config . iLockedCPUSpeed ) ;
2013-06-12 17:15:45 -04:00
}
2019-04-14 14:51:35 -07:00
CoreTiming : : SetClockFrequencyHz ( PowerCpuMhzToHz ( cpufreq , pllFreq ) ) ;
return hleLogSuccessI ( SCEMISC , 0 ) ;
2012-12-26 21:08:41 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerSetBusClockFrequency ( u32 busfreq ) {
2019-04-14 14:51:35 -07:00
if ( busfreq = = 0 | | busfreq > 111 ) {
return hleLogWarning ( SCEMISC , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid frequency " ) ;
2013-06-12 17:15:45 -04:00
}
2019-04-14 14:51:35 -07:00
if ( g_Config . iLockedCPUSpeed > 0 ) {
return hleLogDebug ( SCEMISC , 0 , " locked by user config at %i " , g_Config . iLockedCPUSpeed / 2 ) ;
2013-06-12 17:15:45 -04:00
}
2019-04-14 14:51:35 -07:00
// The value passed is validated, but then doesn't seem to matter for the result.
// However, this sets a different hz than scePowerSetClockFrequency would have.
if ( pllFreq < = 190 )
busFreq = 94956673 ;
else if ( pllFreq < = 222 )
busFreq = 111000000 ;
else if ( pllFreq < = 266 )
busFreq = 132939331 ;
else if ( pllFreq < = 333 )
busFreq = 165848343 ;
else
busFreq = pllFreq / 2 ;
return hleLogSuccessI ( SCEMISC , 0 ) ;
2012-12-26 21:08:41 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerGetCpuClockFrequencyInt ( ) {
2019-04-14 14:51:35 -07:00
int cpuFreq = CoreTiming : : GetClockFrequencyHz ( ) / 1000000 ;
return hleLogSuccessI ( SCEMISC , cpuFreq ) ;
2012-11-12 00:04:57 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerGetPllClockFrequencyInt ( ) {
2019-04-14 14:51:35 -07:00
return hleLogSuccessInfoI ( SCEMISC , pllFreq / 1000000 ) ;
2012-11-12 00:04:57 +01:00
}
2014-12-08 04:40:08 -05:00
static u32 scePowerGetBusClockFrequencyInt ( ) {
2019-04-14 14:51:35 -07:00
return hleLogSuccessInfoI ( SCEMISC , busFreq / 1000000 ) ;
2013-03-19 06:09:37 +08:00
}
2014-12-08 04:40:08 -05:00
static float scePowerGetCpuClockFrequencyFloat ( ) {
2019-04-14 14:51:35 -07:00
float cpuFreq = CoreTiming : : GetClockFrequencyHz ( ) / 1000000.0f ;
DEBUG_LOG ( SCEMISC , " %f=scePowerGetCpuClockFrequencyFloat() " , ( float ) cpuFreq ) ;
return cpuFreq ;
2013-03-19 06:09:37 +08:00
}
2014-12-08 04:40:08 -05:00
static float scePowerGetPllClockFrequencyFloat ( ) {
2019-04-14 14:51:35 -07:00
INFO_LOG ( SCEMISC , " %f=scePowerGetPllClockFrequencyFloat() " , ( float ) pllFreq / 1000000.0f ) ;
return ( float ) pllFreq / 1000000.0f ;
2013-03-04 23:39:59 +01:00
}
2014-12-08 04:40:08 -05:00
static float scePowerGetBusClockFrequencyFloat ( ) {
2019-04-14 14:51:35 -07:00
INFO_LOG ( SCEMISC , " %f=scePowerGetBusClockFrequencyFloat() " , ( float ) busFreq / 1000000.0f ) ;
return ( float ) busFreq / 1000000.0f ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static int scePowerTick ( ) {
2019-04-14 14:51:35 -07:00
DEBUG_LOG ( SCEMISC , " scePowerTick() " ) ;
2013-06-11 11:48:45 +02:00
// Don't think we need to do anything.
return 0 ;
}
2013-03-19 06:09:37 +08:00
2014-12-08 04:40:08 -05:00
static u32 IsPSPNonFat ( ) {
2019-04-14 14:51:35 -07:00
DEBUG_LOG ( SCEMISC , " %d=scePower_a85880d0_IsPSPNonFat() " , g_Config . iPSPModel ) ;
2013-11-28 12:35:15 -05:00
return g_Config . iPSPModel ;
2013-02-02 00:22:01 +01:00
}
2012-12-09 19:41:19 +07:00
static const HLEFunction scePower [ ] = {
2015-03-22 16:57:56 -07:00
{ 0X04B7766E , & WrapI_II < scePowerRegisterCallback > , " scePowerRegisterCallback " , ' i ' , " ii " } ,
{ 0X2B51FE2F , nullptr , " scePower_2B51FE2F " , ' ? ' , " " } ,
{ 0X442BFBAC , nullptr , " scePowerGetBacklightMaximum " , ' ? ' , " " } ,
{ 0XEFD3C963 , & WrapI_V < scePowerTick > , " scePowerTick " , ' i ' , " " } ,
{ 0XEDC13FE5 , nullptr , " scePowerGetIdleTimer " , ' ? ' , " " } ,
{ 0X7F30B3B1 , nullptr , " scePowerIdleTimerEnable " , ' ? ' , " " } ,
{ 0X972CE941 , nullptr , " scePowerIdleTimerDisable " , ' ? ' , " " } ,
{ 0X27F3292C , nullptr , " scePowerBatteryUpdateInfo " , ' ? ' , " " } ,
2021-10-18 06:50:18 -07:00
{ 0XE8E4E204 , nullptr , " scePowerGetForceSuspendCapacity " , ' ? ' , " " } ,
2015-03-22 16:57:56 -07:00
{ 0XB999184C , nullptr , " scePowerGetLowBatteryCapacity " , ' ? ' , " " } ,
{ 0X87440F5E , & WrapI_V < scePowerIsPowerOnline > , " scePowerIsPowerOnline " , ' i ' , " " } ,
{ 0X0AFD0D8B , & WrapI_V < scePowerIsBatteryExist > , " scePowerIsBatteryExist " , ' i ' , " " } ,
{ 0X1E490401 , & WrapI_V < scePowerIsBatteryCharging > , " scePowerIsBatteryCharging " , ' i ' , " " } ,
{ 0XB4432BC8 , & WrapI_V < scePowerGetBatteryChargingStatus > , " scePowerGetBatteryChargingStatus " , ' i ' , " " } ,
{ 0XD3075926 , & WrapI_V < scePowerIsLowBattery > , " scePowerIsLowBattery " , ' i ' , " " } ,
{ 0X78A1A796 , nullptr , " scePowerIsSuspendRequired " , ' ? ' , " " } ,
{ 0X94F5A53F , nullptr , " scePowerGetBatteryRemainCapacity " , ' ? ' , " " } ,
{ 0XFD18A0FF , nullptr , " scePowerGetBatteryFullCapacity " , ' ? ' , " " } ,
{ 0X2085D15D , & WrapI_V < scePowerGetBatteryLifePercent > , " scePowerGetBatteryLifePercent " , ' i ' , " " } ,
{ 0X8EFB3FA2 , & WrapI_V < scePowerGetBatteryLifeTime > , " scePowerGetBatteryLifeTime " , ' i ' , " " } ,
{ 0X28E12023 , & WrapI_V < scePowerGetBatteryTemp > , " scePowerGetBatteryTemp " , ' i ' , " " } ,
{ 0X862AE1A6 , nullptr , " scePowerGetBatteryElec " , ' ? ' , " " } ,
{ 0X483CE86B , nullptr , " scePowerGetBatteryVolt " , ' ? ' , " " } ,
{ 0XCB49F5CE , nullptr , " scePowerGetBatteryChargeCycle " , ' ? ' , " " } ,
{ 0X23436A4A , nullptr , " scePowerGetInnerTemp " , ' ? ' , " " } ,
{ 0X0CD21B1F , nullptr , " scePowerSetPowerSwMode " , ' ? ' , " " } ,
{ 0X165CE085 , nullptr , " scePowerGetPowerSwMode " , ' ? ' , " " } ,
{ 0XD6D016EF , nullptr , " scePowerLock " , ' ? ' , " " } ,
{ 0XCA3D34C1 , nullptr , " scePowerUnlock " , ' ? ' , " " } ,
{ 0XDB62C9CF , nullptr , " scePowerCancelRequest " , ' ? ' , " " } ,
{ 0X7FA406DD , nullptr , " scePowerIsRequest " , ' ? ' , " " } ,
{ 0X2B7C7CF4 , nullptr , " scePowerRequestStandby " , ' ? ' , " " } ,
{ 0XAC32C9CC , nullptr , " scePowerRequestSuspend " , ' ? ' , " " } ,
{ 0X2875994B , nullptr , " scePower_2875994B " , ' ? ' , " " } ,
{ 0X0074EF9B , nullptr , " scePowerGetResumeCount " , ' ? ' , " " } ,
{ 0XDFA8BAF8 , & WrapI_I < scePowerUnregisterCallback > , " scePowerUnregisterCallback " , ' i ' , " i " } ,
{ 0XDB9D28DD , & WrapI_I < scePowerUnregisterCallback > , " scePowerUnregitserCallback " , ' i ' , " i " } ,
{ 0X843FBF43 , & WrapU_U < scePowerSetCpuClockFrequency > , " scePowerSetCpuClockFrequency " , ' x ' , " x " } ,
{ 0XB8D7B3FB , & WrapU_U < scePowerSetBusClockFrequency > , " scePowerSetBusClockFrequency " , ' x ' , " x " } ,
{ 0XFEE03A2F , & WrapU_V < scePowerGetCpuClockFrequencyInt > , " scePowerGetCpuClockFrequency " , ' x ' , " " } ,
{ 0X478FE6F5 , & WrapU_V < scePowerGetBusClockFrequencyInt > , " scePowerGetBusClockFrequency " , ' x ' , " " } ,
{ 0XFDB5BFE9 , & WrapU_V < scePowerGetCpuClockFrequencyInt > , " scePowerGetCpuClockFrequencyInt " , ' x ' , " " } ,
{ 0XBD681969 , & WrapU_V < scePowerGetBusClockFrequencyInt > , " scePowerGetBusClockFrequencyInt " , ' x ' , " " } ,
{ 0XB1A52C83 , & WrapF_V < scePowerGetCpuClockFrequencyFloat > , " scePowerGetCpuClockFrequencyFloat " , ' f ' , " " } ,
{ 0X9BADB3EB , & WrapF_V < scePowerGetBusClockFrequencyFloat > , " scePowerGetBusClockFrequencyFloat " , ' f ' , " " } ,
{ 0X737486F2 , & WrapU_UUU < scePowerSetClockFrequency > , " scePowerSetClockFrequency " , ' x ' , " xxx " } ,
{ 0X34F9C463 , & WrapU_V < scePowerGetPllClockFrequencyInt > , " scePowerGetPllClockFrequencyInt " , ' x ' , " " } ,
{ 0XEA382A27 , & WrapF_V < scePowerGetPllClockFrequencyFloat > , " scePowerGetPllClockFrequencyFloat " , ' f ' , " " } ,
2021-10-18 06:50:18 -07:00
{ 0XEBD177D6 , & WrapU_UUU < scePowerSetClockFrequency > , " scePowerSetClockFrequency350 " , ' x ' , " xxx " } , // This is also the same as SetClockFrequency
2015-03-22 16:57:56 -07:00
{ 0X469989AD , & WrapU_UUU < scePowerSetClockFrequency > , " scePower_469989ad " , ' x ' , " xxx " } , // This is also the same as SetClockFrequency
{ 0X545A7F3C , nullptr , " scePower_545A7F3C " , ' ? ' , " " } , // TODO: Supposedly the same as SetClockFrequency also?
{ 0XA4E93389 , nullptr , " scePower_A4E93389 " , ' ? ' , " " } , // TODO: Supposedly the same as SetClockFrequency also?
{ 0XA85880D0 , & WrapU_V < IsPSPNonFat > , " scePower_a85880d0_IsPSPNonFat " , ' x ' , " " } ,
{ 0X3951AF53 , nullptr , " scePowerWaitRequestCompletion " , ' ? ' , " " } ,
{ 0X0442D852 , nullptr , " scePowerRequestColdReset " , ' ? ' , " " } ,
{ 0XBAFA3DF0 , nullptr , " scePowerGetCallbackMode " , ' ? ' , " " } ,
{ 0XA9D22232 , nullptr , " scePowerSetCallbackMode " , ' ? ' , " " } ,
2013-08-26 01:04:37 -07:00
// These seem to be aliases.
2015-03-22 16:57:56 -07:00
{ 0X23C31FFE , & WrapI_IUU < sceKernelVolatileMemLock > , " scePowerVolatileMemLock " , ' i ' , " ixx " } ,
{ 0XFA97A599 , & WrapI_IUU < sceKernelVolatileMemTryLock > , " scePowerVolatileMemTryLock " , ' i ' , " ixx " } ,
{ 0XB3EDD801 , & WrapI_I < sceKernelVolatileMemUnlock > , " scePowerVolatileMemUnlock " , ' i ' , " i " } ,
2012-11-01 16:19:01 +01:00
} ;
//890129c in tyshooter looks bogus
2012-12-09 19:41:19 +07:00
const HLEFunction sceSuspendForUser [ ] = {
2015-03-22 16:57:56 -07:00
{ 0XEADB1BD7 , & WrapI_I < sceKernelPowerLock > , " sceKernelPowerLock " , ' i ' , " i " } , //(int param) set param to 0
{ 0X3AEE7261 , & WrapI_I < sceKernelPowerUnlock > , " sceKernelPowerUnlock " , ' i ' , " i " } , //(int param) set param to 0
{ 0X090CCB3F , & WrapI_I < sceKernelPowerTick > , " sceKernelPowerTick " , ' i ' , " i " } ,
2012-11-11 21:32:44 +00:00
// There's an extra 4MB that can be allocated, which seems to be "volatile". These functions
// let you grab it.
2015-03-22 16:57:56 -07:00
{ 0XA14F40B2 , & WrapI_IUU < sceKernelVolatileMemTryLock > , " sceKernelVolatileMemTryLock " , ' i ' , " ixx " } ,
{ 0XA569E425 , & WrapI_I < sceKernelVolatileMemUnlock > , " sceKernelVolatileMemUnlock " , ' i ' , " i " } ,
{ 0X3E0271D3 , & WrapI_IUU < sceKernelVolatileMemLock > , " sceKernelVolatileMemLock " , ' i ' , " ixx " } ,
2012-11-01 16:19:01 +01:00
} ;
void Register_scePower ( ) {
2012-11-11 21:32:44 +00:00
RegisterModule ( " scePower " , ARRAY_SIZE ( scePower ) , scePower ) ;
2012-11-01 16:19:01 +01:00
}
void Register_sceSuspendForUser ( ) {
2012-11-11 21:32:44 +00:00
RegisterModule ( " sceSuspendForUser " , ARRAY_SIZE ( sceSuspendForUser ) , sceSuspendForUser ) ;
2012-11-01 16:19:01 +01:00
}