193 lines
6.6 KiB
C++
193 lines
6.6 KiB
C++
/*
|
|
* Copyright (c) Horizon OC Contributors
|
|
*
|
|
* Copyright (c) 2023 hanai3Bi
|
|
*
|
|
* 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 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pcv.hpp"
|
|
|
|
namespace ams::ldr::oc::pcv {
|
|
|
|
Result MemFreqPllmLimit(u32* ptr) {
|
|
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr);
|
|
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
|
|
|
|
// Double the max clk simply
|
|
u32 max_clk = entry->freq * 2;
|
|
entry->freq = max_clk;
|
|
entry->vco_max = max_clk;
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result MemVoltHandler(u32* ptr) {
|
|
// ptr value might be default_uv or max_uv
|
|
regulator* entries[2] = {
|
|
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.default_uv)),
|
|
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.max_uv)),
|
|
};
|
|
|
|
constexpr u32 uv_step = 12'500;
|
|
constexpr u32 uv_min = 600'000;
|
|
|
|
auto validator = [](regulator* entry) {
|
|
R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry());
|
|
R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry());
|
|
R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry());
|
|
R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
|
R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
|
R_SUCCEED();
|
|
};
|
|
|
|
regulator* entry = nullptr;
|
|
for (auto& i : entries) {
|
|
if (R_SUCCEEDED(validator(i)))
|
|
entry = i;
|
|
}
|
|
|
|
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
|
|
|
|
u32 emc_uv = C.commonEmcMemVolt;
|
|
if (!emc_uv)
|
|
R_SKIP();
|
|
|
|
if (emc_uv % uv_step)
|
|
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
|
|
|
PATCH_OFFSET(ptr, emc_uv);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
void SafetyCheck() {
|
|
if (C.custRev != CUST_REV)
|
|
CRASH("Triggered");
|
|
|
|
struct sValidator {
|
|
volatile u32 value;
|
|
u32 min;
|
|
u32 max;
|
|
bool value_required = false;
|
|
|
|
Result check() {
|
|
if (!value_required && !value)
|
|
R_SUCCEED();
|
|
|
|
if (min && value < min)
|
|
R_THROW(ldr::ResultSafetyCheckFailure());
|
|
if (max && value > max)
|
|
R_THROW(ldr::ResultSafetyCheckFailure());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
};
|
|
u32 eristaCpuDvfsMaxFreq;
|
|
if (C.enableEristaCpuUnsafeFreqs) {
|
|
eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTableUnsafeFreqs)->freq);
|
|
} else {
|
|
eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
|
}
|
|
u32 marikoCpuDvfsMaxFreq;
|
|
if(C.enableMarikoCpuUnsafeFreqs) {
|
|
marikoCpuDvfsMaxFreq = static_cast<u32>(C.marikoCpuUV ? GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq : GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
|
} else {
|
|
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTableUnsafeFreqs)->freq);
|
|
}
|
|
u32 eristaGpuDvfsMaxFreq;
|
|
switch (C.eristaGpuUV)
|
|
{
|
|
case 0:
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
|
break;
|
|
case 1:
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq);
|
|
break;
|
|
case 2:
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHigh)->freq);
|
|
break;
|
|
case 3:
|
|
if(C.enableEristaGpuUnsafeFreqs)
|
|
{
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableUv3UnsafeFreqs)->freq);
|
|
}
|
|
else
|
|
{
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
|
}
|
|
break;
|
|
default:
|
|
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
|
break;
|
|
}
|
|
|
|
u32 marikoGpuDvfsMaxFreq;
|
|
switch (C.marikoGpuUV) {
|
|
case 0:
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
|
break;
|
|
case 1:
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
|
|
break;
|
|
case 2:
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
|
|
break;
|
|
case 3:
|
|
if(C.enableMarikoGpuUnsafeFreqs)
|
|
{
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableUv3UnsafeFreqs)->freq);
|
|
}
|
|
else
|
|
{
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
|
}
|
|
break;
|
|
default:
|
|
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
|
break;
|
|
}
|
|
|
|
sValidator validators[] = {
|
|
{ C.commonCpuBoostClock, 1020'000, 3000'000, true },
|
|
{ C.commonEmcMemVolt, 1100'000, 1500'000 }, // Official burst vmax for the RAMs
|
|
{ C.eristaCpuMaxVolt, 1100, 1300 },
|
|
{ C.eristaEmcClock1, 1600'000, 2600'200 },
|
|
{ C.eristaEmcClock2, 1600'000, 2600'200 },
|
|
{ C.eristaEmcClock3, 1600'000, 2600'200 },
|
|
{ C.marikoCpuMaxVolt, 1100, 1300 },
|
|
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
|
|
{ C.marikoEmcVddqVolt, 550'000, 700'000 },
|
|
{ eristaCpuDvfsMaxFreq, 1785'000, 3000'000 },
|
|
{ marikoCpuDvfsMaxFreq, 1785'000, 3000'000 },
|
|
{ eristaGpuDvfsMaxFreq, 768'000, 1228'000 },
|
|
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
|
|
};
|
|
|
|
for (auto& i : validators) {
|
|
if (R_FAILED(i.check()))
|
|
CRASH("Triggered");
|
|
}
|
|
}
|
|
|
|
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
|
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
|
SafetyCheck();
|
|
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
|
if (isMariko)
|
|
mariko::Patch(mapped_nso, nso_size);
|
|
else
|
|
erista::Patch(mapped_nso, nso_size);
|
|
#endif
|
|
}
|
|
|
|
} |