/* * -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , * wrote this file. As long as you retain this notice you can do whatever you * want with this stuff. If you meet any of us some day, and you think this * stuff is worth it, you can buy us a beer in return. - The sys-clk authors * -------------------------------------------------------------------------- */ #include #include #include "clocks.h" #include "errors.h" void Clocks::GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min, uint32_t** max) { if (module == SysClkModule_MEM) { *min = isMariko ? &g_freq_table_mem_hz[4] : &g_freq_table_mem_hz[0]; // 1600 / 665 *max = &g_freq_table_mem_hz[5]; // 1862 - Max return; } if (module == SysClkModule_CPU) { *min = &g_freq_table_cpu_hz[0]; if (!isMariko) *max = &g_freq_table_cpu_hz[11]; // 1785 else { if (allowUnsafe) *max = &g_freq_table_cpu_hz[17]; // 2397 else *max = &g_freq_table_cpu_hz[13]; // 1963 } return; } if (module == SysClkModule_GPU) { *min = &g_freq_table_gpu_hz[0]; if (isMariko && !allowUnsafe) { *max = &g_freq_table_gpu_hz[11]; // 921 return; } switch (profile) { case SysClkProfile_Handheld: *max = isMariko ? &g_freq_table_gpu_hz[11] : &g_freq_table_gpu_hz[5]; // 921 / 460 break; case SysClkProfile_HandheldChargingUSB: *max = isMariko ? &g_freq_table_gpu_hz[16] : &g_freq_table_gpu_hz[9]; // 1267 / 768 break; default: *max = isMariko ? &g_freq_table_gpu_hz[17] : &g_freq_table_gpu_hz[11]; // 1305 / 921 break; } return; } ERROR_THROW("No such PcvModule: %u", module); } Result Clocks::GetTable(SysClkModule module, SysClkProfile profile, size_t max_entry_num, uint32_t* out_table) { uint32_t* min = NULL; uint32_t* max = NULL; memset(out_table, 0, max_entry_num * sizeof(uint32_t)); GetRange(module, profile, &min, &max); if (!min || !max || (max - min) / sizeof(uint32_t) >= max_entry_num) return 1; uint32_t* p = min; size_t idx = 0; while(p <= max) out_table[idx++] = *p++; return 0; } void Clocks::Initialize() { Result rc = 0; // Check if it's Mariko u64 hardware_type = 0; splInitialize(); splGetConfig(SplConfigItem_HardwareType, &hardware_type); splExit(); switch (hardware_type) { case 1: //Icosa case 2: //Copper isMariko = false; break; case 3: // Iowa case 4: // Hoag case 5: // Aula isMariko = true; break; default: ERROR_THROW("Unknown hardware type: 0x%X!", hardware_type); return; } if(hosversionAtLeast(8,0,0)) { rc = clkrstInitialize(); ASSERT_RESULT_OK(rc, "pcvInitialize"); } else { rc = pcvInitialize(); ASSERT_RESULT_OK(rc, "pcvInitialize"); } rc = apmExtInitialize(); ASSERT_RESULT_OK(rc, "apmExtInitialize"); rc = psmInitialize(); ASSERT_RESULT_OK(rc, "psmInitialize"); rc = tsInitialize(); ASSERT_RESULT_OK(rc, "tsInitialize"); if(hosversionAtLeast(5,0,0)) { rc = tcInitialize(); ASSERT_RESULT_OK(rc, "tcInitialize"); } } void Clocks::Exit() { if(hosversionAtLeast(8,0,0)) { pcvExit(); } else { clkrstExit(); } apmExtExit(); psmExit(); tsExit(); if(hosversionAtLeast(5,0,0)) { tcExit(); } } const char* Clocks::GetModuleName(SysClkModule module, bool pretty) { const char* result = sysclkFormatModule(module, pretty); if(!result) { ERROR_THROW("No such SysClkModule: %u", module); } return result; } const char* Clocks::GetProfileName(SysClkProfile profile, bool pretty) { const char* result = sysclkFormatProfile(profile, pretty); if(!result) { ERROR_THROW("No such SysClkProfile: %u", profile); } return result; } const char* Clocks::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty) { const char* result = sysclkFormatThermalSensor(sensor, pretty); if(!result) { ERROR_THROW("No such SysClkThermalSensor: %u", sensor); } return result; } PcvModule Clocks::GetPcvModule(SysClkModule sysclkModule) { switch(sysclkModule) { case SysClkModule_CPU: return PcvModule_CpuBus; case SysClkModule_GPU: return PcvModule_GPU; case SysClkModule_MEM: return PcvModule_EMC; default: ERROR_THROW("No such SysClkModule: %u", sysclkModule); } return (PcvModule)0; } PcvModuleId Clocks::GetPcvModuleId(SysClkModule sysclkModule) { PcvModuleId pcvModuleId; Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(sysclkModule)); ASSERT_RESULT_OK(rc, "pcvGetModuleId"); return pcvModuleId; } SysClkApmConfiguration* Clocks::GetEmbeddedApmConfig(uint32_t confId) { SysClkApmConfiguration* apmConfiguration = NULL; for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) { if(sysclk_g_apm_configurations[i].id == confId) { apmConfiguration = &sysclk_g_apm_configurations[i]; break; } } if(!apmConfiguration) { ERROR_THROW("Unknown apm configuration: %x", confId); } return apmConfiguration; } uint32_t Clocks::GetStockClock(SysClkApmConfiguration* apm, SysClkModule module) { switch (module) { case SysClkModule_CPU: return apm->cpu_hz; case SysClkModule_GPU: return apm->gpu_hz; case SysClkModule_MEM: return apm->mem_hz; default: ERROR_THROW("Unknown SysClkModule: %x", module); return 0; } } void Clocks::ResetToStock(unsigned int module) { if(hosversionAtLeast(9,0,0)) { std::uint32_t confId = 0; Result rc = apmExtGetCurrentPerformanceConfiguration(&confId); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); SysClkApmConfiguration* apmConfiguration = GetEmbeddedApmConfig(confId); if (module == SysClkModule_EnumMax || module == SysClkModule_CPU) { Clocks::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); } if (module == SysClkModule_EnumMax || module == SysClkModule_GPU) { Clocks::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); } if (module == SysClkModule_EnumMax || module == SysClkModule_MEM) { Clocks::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); } } else { Result rc = 0; std::uint32_t mode = 0; rc = apmExtGetPerformanceMode(&mode); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); rc = apmExtSysRequestPerformanceMode(mode); ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); } } SysClkProfile Clocks::GetCurrentProfile() { std::uint32_t mode = 0; Result rc = apmExtGetPerformanceMode(&mode); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); if(mode) { return SysClkProfile_Docked; } PsmChargerType chargerType; rc = psmGetChargerType(&chargerType); ASSERT_RESULT_OK(rc, "psmGetChargerType"); switch(chargerType) { case PsmChargerType_EnoughPower: return SysClkProfile_HandheldChargingOfficial; case PsmChargerType_LowPower: case PsmChargerType_NotSupported: return SysClkProfile_HandheldChargingUSB; default: return SysClkProfile_Handheld; } } void Clocks::SetHz(SysClkModule module, std::uint32_t hz) { Result rc = 0; if(hosversionAtLeast(8,0,0)) { ClkrstSession session = {0}; rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3); ASSERT_RESULT_OK(rc, "clkrstOpenSession"); rc = clkrstSetClockRate(&session, hz); ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); clkrstCloseSession(&session); } else { rc = pcvSetClockRate(Clocks::GetPcvModule(module), hz); ASSERT_RESULT_OK(rc, "pcvSetClockRate"); } } std::uint32_t Clocks::GetCurrentHz(SysClkModule module) { Result rc = 0; std::uint32_t hz = 0; if(hosversionAtLeast(8,0,0)) { ClkrstSession session = {0}; rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3); ASSERT_RESULT_OK(rc, "clkrstOpenSession"); rc = clkrstGetClockRate(&session, &hz); ASSERT_RESULT_OK(rc, "clkrstGetClockRate"); clkrstCloseSession(&session); } else { rc = pcvGetClockRate(Clocks::GetPcvModule(module), &hz); ASSERT_RESULT_OK(rc, "pcvGetClockRate"); } return hz; } std::uint32_t Clocks::GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz) { uint32_t* min = NULL; uint32_t* max = NULL; GetRange(module, profile, &min, &max); if (!min || !max) ERROR_THROW("table lookup failed for SysClkModule: %u", module); uint32_t inMHz = inHz / 1000000U; uint32_t* p = min; while(p <= max) { if (inMHz == *p / 1000000U) return *p; p++; } return *max; } std::int32_t Clocks::GetTsTemperatureMilli(TsLocation location) { Result rc; std::int32_t millis = 0; if(hosversionAtLeast(14,0,0)) { rc = tsGetTemperature(location, &millis); ASSERT_RESULT_OK(rc, "tsGetTemperature(%u)", location); millis *= 1000; } else { rc = tsGetTemperatureMilliC(location, &millis); ASSERT_RESULT_OK(rc, "tsGetTemperatureMilliC(%u)", location); } return millis; } std::uint32_t Clocks::GetTemperatureMilli(SysClkThermalSensor sensor) { std::int32_t millis = 0; if(sensor == SysClkThermalSensor_SOC) { millis = GetTsTemperatureMilli(TsLocation_External); } else if(sensor == SysClkThermalSensor_PCB) { millis = GetTsTemperatureMilli(TsLocation_Internal); } else if(sensor == SysClkThermalSensor_Skin) { if(hosversionAtLeast(5,0,0)) { Result rc = tcGetSkinTemperatureMilliC(&millis); ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC"); } } else { ERROR_THROW("No such SysClkThermalSensor: %u", sensor); } return std::max(0, millis); }