/* * 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 . */ #include #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(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(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(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(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(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(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(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)); } } }