Atmosphere/exosphere/warmboot/source/warmboot_bootrom_workaround.cpp
2020-06-14 22:07:45 -07:00

100 lines
7.0 KiB
C++

/*
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "warmboot_clkrst.hpp"
namespace ams::warmboot {
namespace {
constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress();
constexpr inline const uintptr_t EMC = EMC_ADDRESS(0);
}
void ApplyMbistWorkaround() {
/* Clear all LVL2 clock gate overrides to zero. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA, 0);
reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB, 0);
reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC, 0);
reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, 0);
reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE, 0);
/* Clear clock enable for all but a select few devices. */
auto devices_to_clear_l = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_l), CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_RTC ),
CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_TMR ),
CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_GPIO ),
CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_CACHE2));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, devices_to_clear_l);
auto devices_to_clear_h = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_h), CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_MEM ),
CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_PMC ),
CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_FUSE),
CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_EMC ));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, devices_to_clear_h);
auto devices_to_clear_u = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_u), CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CSITE),
CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMA),
CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMB),
CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMC),
CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMD),
CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CRAM2));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, devices_to_clear_u);
auto devices_to_clear_v = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_v), CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_MSELECT ),
CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SPDIF_DOUBLER),
CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_TZRAM ),
CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SE ));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_CLR, devices_to_clear_v);
auto devices_to_clear_w = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_w), CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX0),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX1),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX2),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX3),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX4),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX5),
CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_ENTROPY));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, devices_to_clear_w);
auto devices_to_clear_x = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_X);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_x), CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CAPA ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CBPA ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CPU ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_BBC ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_EMC_DLL ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_GPU ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_DBGAPB ),
CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_PLLG_REF));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_X_CLR, devices_to_clear_x);
auto devices_to_clear_y = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_Y);
reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_y), CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CCPA),
CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CDPA));
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_CLR, devices_to_clear_y);
/* If CH1 is enabled, enable clock to MC1. */
if (reg::HasValue(EMC + EMC_FBIO_CFG7, EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))) {
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLK_ENB_MC1, ENABLE));
}
}
}