/* * Copyright (c) Atmosphère-NX * * 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 . */ #include namespace ams::nxboot { void DoMemoryTrainingErista(int index, void *mtc_tables_buffer); void DoMemoryTrainingMariko(bool *out_did_training, int index, void *mtc_tables_buffer); void RestoreMemoryClockRateMariko(void *mtc_tables_buffer); namespace { alignas(4) constinit u8 g_mtc_tables_buffer[0x26C0]; constinit bool g_did_training_mariko = false; constexpr const u8 MemoryTrainingTableIndex_Invalid = std::numeric_limits::max(); constexpr const u8 MemoryTrainingTableIndices[] = { /* DramId_EristaIcosaSamsung4gb */ 0x00, /* DramId_EristaIcosaHynix4gb */ 0x02, /* DramId_EristaIcosaMicron4gb */ 0x03, /* DramId_MarikoIowaHynix1y4gb */ 0x10, /* DramId_EristaIcosaSamsung6gb */ 0x01, /* DramId_MarikoHoagHynix1y4gb */ 0x10, /* DramId_MarikoAulaHynix1y4gb */ 0x10, /* DramId_MarikoIowax1x2Samsung4gb */ 0x00, /* DramId_MarikoIowaSamsung4gb */ 0x05, /* DramId_MarikoIowaSamsung8gb */ 0x06, /* DramId_MarikoIowaHynix4gb */ 0x07, /* DramId_MarikoIowaMicron4gb */ 0x08, /* DramId_MarikoHoagSamsung4gb */ 0x05, /* DramId_MarikoHoagSamsung8gb */ 0x06, /* DramId_MarikoHoagHynix4gb */ 0x07, /* DramId_MarikoHoagMicron4gb */ 0x08, /* DramId_MarikoIowaSamsung4gbY */ 0x09, /* DramId_MarikoIowaSamsung1y4gbX */ 0x0C, /* DramId_MarikoIowaSamsung1y8gbX */ 0x0D, /* DramId_MarikoHoagSamsung1y4gbX */ 0x0C, /* DramId_MarikoIowaSamsung1z4gb */ 0x12, /* DramId_MarikoHoagSamsung1z4gb */ 0x12, /* DramId_MarikoAulaSamsung1z4gb */ 0x12, /* DramId_MarikoHoagSamsung1y8gbX */ 0x0D, /* DramId_MarikoAulaSamsung1y4gbX */ 0x0C, /* DramId_MarikoIowaMicron1y4gb */ 0x0F, /* DramId_MarikoHoagMicron1y4gb */ 0x0F, /* DramId_MarikoAulaMicron1y4gb */ 0x0F, /* DramId_MarikoAulaSamsung1y8gbX */ 0x0D, }; int GetMemoryTrainingTableIndex() { if (const auto dram_id = fuse::GetDramId(); dram_id < util::size(MemoryTrainingTableIndices) && MemoryTrainingTableIndices[dram_id] != MemoryTrainingTableIndex_Invalid) { return static_cast(MemoryTrainingTableIndices[dram_id]); } else { return -1; } } } void DoMemoryTraining() { const auto index = GetMemoryTrainingTableIndex(); if (fuse::GetSocType() == fuse::SocType_Erista) { DoMemoryTrainingErista(index, g_mtc_tables_buffer); } else { DoMemoryTrainingMariko(std::addressof(g_did_training_mariko), index, g_mtc_tables_buffer); } } void RestoreMemoryClockRate() { /* NOTE: This resolves an off-by-one issue in PCV's detection of memory clock rate on Mariko. */ if (fuse::GetSocType() == fuse::SocType_Mariko && g_did_training_mariko) { RestoreMemoryClockRateMariko(g_mtc_tables_buffer); } } }