exo2: implement remainder of warmboot tz code

This commit is contained in:
Michael Scire 2020-05-13 10:56:07 -07:00 committed by SciresM
parent 97ab282351
commit ad664daea5
21 changed files with 691 additions and 17 deletions

View File

@ -32,14 +32,14 @@ namespace ams::secmon {
void Main() {
/* Set library register addresses. */
/* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */
actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress());
clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress());
flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress());
fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress());
gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress());
i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress());
i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress());
/* pinmux::SetRegisterAddress(); */
pinmux::SetRegisterAddress(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress());
pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress());
se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress());
uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress());

View File

@ -28,6 +28,17 @@ namespace ams::secmon {
InvalidateL3Entries(l3, boot_code, boot_code_size);
}
constexpr void UnmapTzramImpl(u64 *l1, u64 *l2, u64 *l3) {
/* Unmap the L3 entries corresponding to tzram. */
InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize());
/* Unmap the L2 entries corresponding to those L3 entries. */
InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize());
/* Unmap the L1 entry corresponding to to those L2 entries. */
InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize());
}
}
void UnmapBootCode() {
@ -49,4 +60,16 @@ namespace ams::secmon {
secmon::EnsureMappingConsistency();
}
void UnmapTzram() {
/* Get the tables. */
u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
/* Unmap. */
UnmapTzramImpl(l1, l2_l3, l2_l3);
/* Ensure the mappings are consistent. */
secmon::EnsureMappingConsistency();
}
}

View File

@ -18,6 +18,6 @@
namespace ams::secmon {
/* TODO */
void UnmapTzram();
}

View File

@ -16,9 +16,12 @@
#include <exosphere.hpp>
#include "secmon_setup.hpp"
#include "secmon_error.hpp"
#include "secmon_map.hpp"
#include "secmon_cpu_context.hpp"
#include "secmon_interrupt_handler.hpp"
#include "secmon_misc.hpp"
#include "smc/secmon_smc_power_management.hpp"
#include "smc/secmon_smc_se_lock.hpp"
namespace ams::secmon {
@ -887,11 +890,58 @@ namespace ams::secmon {
reg::Read (MC + MC_SMMU_TLB_CONFIG);
}
void ValidateResetExpected() {
/* We're coming out of reset, so check that we expected to come out of reset. */
if (!IsResetExpected()) {
secmon::SetError(pkg1::ErrorInfo_UnexpectedReset);
AMS_ABORT("unexpected reset");
}
SetResetExpected(false);
}
void ActmonInterruptHandler() {
SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt);
AMS_ABORT("actmon observed bpmp wakeup");
}
void ExitChargerHiZMode() {
/* Setup I2c-1. */
pinmux::SetupI2c1();
clkrst::EnableI2c1Clock();
/* Initialize I2c-1. */
i2c::Initialize(i2c::Port_1);
/* Exit Hi-Z mode. */
charger::ExitHiZMode();
/* Disable clock to I2c-1. */
clkrst::DisableI2c1Clock();
}
bool IsExitLp0() {
return reg::Read(MC + MC_SECURITY_CFG3) == 0;
}
void LogExitLp0() {
/* NOTE: Nintendo only does this on dev, but we will always do it. */
if (true /* !pkg1::IsProduction() */) {
log::Initialize();
log::SendText("OHAYO\n", 6);
log::Flush();
}
}
void SetupForLp0Exit() {
/* Exit HiZ mode in charger, if we need to. */
if (smc::IsChargerHiZModeEnabled()) {
ExitChargerHiZMode();
}
/* Unlock the security engine. */
secmon::smc::UnlockSecurityEngine();
}
}
void Setup1() {
@ -908,6 +958,14 @@ namespace ams::secmon {
gic::InitializeCommon();
}
void Setup1ForWarmboot() {
/* Initialize the security engine. */
se::Initialize();
/* Initialize the gic. */
gic::InitializeCommon();
}
void SaveSecurityEngineAesKeySlotTestVector() {
GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector));
}
@ -1007,7 +1065,36 @@ namespace ams::secmon {
}
void SetupSocSecurityWarmboot() {
/* ... */
/* Check that we're allowed to continue. */
ValidateResetExpected();
/* Unmap the tzram identity mapping. */
UnmapTzram();
/* If we're exiting LP0, there's a little more work for us to do. */
if (IsExitLp0()) {
/* Log that we're exiting LP0. */
LogExitLp0();
/* Perform initial setup. */
Setup1ForWarmboot();
/* Setup the Soc security. */
SetupSocSecurity();
/* Set the PMC and MC as secure-only. */
SetupPmcAndMcSecure();
/* Perform Lp0-exit specific init. */
SetupForLp0Exit();
/* Setup the Soc protections. */
SetupSocProtections();
}
/* Perform remaining CPU initialization. */
SetupCpuCoreContext();
SetupCpuSErrorDebug();
}
void SetupSocProtections() {

View File

@ -19,6 +19,12 @@
namespace ams::secmon::smc {
namespace {
constinit bool g_charger_hi_z_mode_enabled = false;
}
SmcResult SmcPowerOffCpu(const SmcArguments &args) {
/* TODO */
return SmcResult::NotImplemented;
@ -34,4 +40,12 @@ namespace ams::secmon::smc {
return SmcResult::NotImplemented;
}
bool IsChargerHiZModeEnabled() {
return g_charger_hi_z_mode_enabled;
}
void SetChargerHiZModeEnabled(bool en) {
g_charger_hi_z_mode_enabled = en;
}
}

View File

@ -24,4 +24,7 @@ namespace ams::secmon::smc {
SmcResult SmcSuspendCpu(const SmcArguments &args);
bool IsChargerHiZModeEnabled();
void SetChargerHiZModeEnabled(bool en);
}

View File

@ -0,0 +1,41 @@
/*
* 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 "../secmon_error.hpp"
#include "secmon_smc_se_lock.hpp"
namespace ams::secmon::smc {
namespace {
constinit std::atomic_bool g_is_locked = false;
}
bool TryLockSecurityEngine() {
bool value = false;
return g_is_locked.compare_exchange_strong(value, true);
}
void UnlockSecurityEngine() {
g_is_locked = false;
}
bool IsSecurityEngineLocked() {
return g_is_locked;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
bool TryLockSecurityEngine();
void UnlockSecurityEngine();
bool IsSecurityEngineLocked();
}

View File

@ -22,6 +22,7 @@
#include <exosphere/util.hpp>
#include <exosphere/mmu.hpp>
#include <exosphere/br.hpp>
#include <exosphere/charger.hpp>
#include <exosphere/gic.hpp>
#include <exosphere/wdt.hpp>
#include <exosphere/pkg1.hpp>
@ -32,6 +33,7 @@
#include <exosphere/fuse.hpp>
#include <exosphere/i2c.hpp>
#include <exosphere/uart.hpp>
#include <exosphere/pinmux.hpp>
#include <exosphere/pmic.hpp>
#include <exosphere/log.hpp>
#include <exosphere/clkrst.hpp>

View File

@ -0,0 +1,25 @@
/*
* 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/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::charger {
bool IsHiZMode();
void EnterHiZMode();
void ExitHiZMode();
}

View File

@ -26,5 +26,8 @@ namespace ams::clkrst {
void EnableUartBClock();
void EnableUartCClock();
void EnableActmonClock();
void EnableI2c1Clock();
void DisableI2c1Clock();
}

View File

@ -19,5 +19,9 @@
namespace ams::log {
void Initialize();
void Finalize();
void SendText(const void *text, size_t size);
void Flush();
}

View File

@ -0,0 +1,29 @@
/*
* 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/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::pinmux {
void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address);
void SetupUartA();
void SetupUartB();
void SetupUartC();
void SetupI2c1();
void SetupI2c5();
}

View File

@ -56,12 +56,16 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1);
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
/* CLK_SOURCE */
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3e8)
/* CLK_ENB_*_INDEX */
#define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C)
#define CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX (0x0F)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17)
@ -80,8 +84,11 @@ DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, O
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2)
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2)
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2)
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2)
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2);

View File

@ -39,4 +39,8 @@ namespace ams::uart {
void Initialize(Port port, int baud_rate, u32 flags);
void SendText(Port port, const void *data, size_t size);
void WaitFlush(Port port);
}

View File

@ -0,0 +1,55 @@
/*
* 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>
namespace ams::charger {
namespace {
/* https://www.ti.com/lit/ds/symlink/bq24193.pdf */
constexpr inline int I2cAddressBq24193 = 0x6B;
constexpr inline int Bq24193RegisterInputSourceControl = 0x00;
/* 8.5.1.1 EN_HIZ */
enum EnHiZ : u8 {
EnHiZ_Disable = (0u << 7),
EnHiZ_Enable = (1u << 7),
EnHiZ_Mask = (1u << 7),
};
}
bool IsHiZMode() {
return (i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl) & EnHiZ_Mask) == EnHiZ_Enable;
}
void EnterHiZMode() {
u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl);
ctrl &= ~EnHiZ_Mask;
ctrl |= EnHiZ_Enable;
i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl);
}
void ExitHiZMode() {
u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl);
ctrl &= ~EnHiZ_Mask;
ctrl |= EnHiZ_Disable;
i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl);
}
}

View File

@ -49,13 +49,13 @@ namespace ams::clkrst {
reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 0));
}
// void DisableClock(const ClockParameters &param) {
// /* Hold reset. */
// reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1));
//
// /* Disable clock. */
// reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0));
// }
void DisableClock(const ClockParameters &param) {
/* Hold reset. */
reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1));
/* Disable clock. */
reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0));
}
#define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \
constexpr inline const ClockParameters _VARNAME_ = { \
@ -70,6 +70,8 @@ namespace ams::clkrst {
DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0);
DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0);
DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0);
DEFINE_CLOCK_PARAMETERS(I2c1Clock, L, I2C1, CLK_M, 0);
DEFINE_CLOCK_PARAMETERS(I2c5Clock, H, I2C5, CLK_M, 0);
DEFINE_CLOCK_PARAMETERS(ActmonClock, V, ACTMON, CLK_M, 0);
}
@ -98,4 +100,16 @@ namespace ams::clkrst {
EnableClock(ActmonClock);
}
void EnableI2c1Clock() {
EnableClock(I2c1Clock);
}
void EnableI2c5Clock() {
EnableClock(I2c1Clock);
}
void DisableI2c1Clock() {
DisableClock(I2c1Clock);
}
}

View File

@ -40,9 +40,30 @@ namespace ams::log {
}
}();
ALWAYS_INLINE void SetupUart() {
if constexpr (UartLogPort == uart::Port_ReservedDebug) {
/* Logging to the debug port. */
pinmux::SetupUartA();
clkrst::EnableUartAClock();
} else if constexpr (UartLogPort == uart::Port_LeftJoyCon) {
/* Logging to left joy-con (e.g. with Joyless). */
pinmux::SetupUartB();
clkrst::EnableUartBClock();
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) {
/* Logging to right joy-con (e.g. with Joyless). */
pinmux::SetupUartC();
clkrst::EnableUartCClock();
} else {
__builtin_unreachable();
}
}
}
void Initialize() {
/* Initialize pinmux and clock for the target uart port. */
SetupUart();
/* Initialize the target uart port. */
uart::Initialize(UartLogPort, 115200, UartPortFlags);
@ -50,4 +71,20 @@ namespace ams::log {
g_initialized_uart = true;
}
void Finalize() {
g_initialized_uart = false;
}
void SendText(const void *text, size_t size) {
if (g_initialized_uart) {
uart::SendText(UartLogPort, text, size);
}
}
void Flush() {
if (g_initialized_uart) {
uart::WaitFlush(UartLogPort);
}
}
}

View File

@ -0,0 +1,181 @@
/*
* 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 "pinmux_registers.hpp"
namespace ams::pinmux {
namespace {
constinit uintptr_t g_pinmux_address = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress();
constinit uintptr_t g_gpio_address = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress();
}
void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address) {
g_pinmux_address = pinmux_address;
g_gpio_address = gpio_address;
}
void SetupUartA() {
/* Get the registers. */
const uintptr_t PINMUX = g_pinmux_address;
/* Configure Uart-A. */
reg::Write(PINMUX + PINMUX_AUX_UART1_TX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART1_RX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA),
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART1_RTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART1_CTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA),
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
}
void SetupUartB() {
/* Get the registers. */
const uintptr_t PINMUX = g_pinmux_address;
/* Configure Uart-B. */
reg::Write(PINMUX + PINMUX_AUX_UART2_TX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART2_RX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART2_RTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART2_CTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
/* Configure GPIO for Uart-B. */
reg::ReadWrite(g_gpio_address + 0x108, REG_BITS_VALUE(0, 4, 0));
}
void SetupUartC() {
/* Get the registers. */
const uintptr_t PINMUX = g_pinmux_address;
/* Configure Uart-B. */
reg::Write(PINMUX + PINMUX_AUX_UART3_TX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART3_RX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART3_RTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_UART3_CTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
/* Configure GPIO for Uart-C. */
reg::ReadWrite(g_gpio_address + 0x00C, REG_BITS_VALUE(1, 4, 0));
}
void SetupI2c1() {
/* Get the registers. */
const uintptr_t PINMUX = g_pinmux_address;
/* Configure I2c1 */
reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
}
void SetupI2c5() {
/* Get the registers. */
const uintptr_t PINMUX = g_pinmux_address;
/* Configure I2c5 */
reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU),
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
}
}

View File

@ -0,0 +1,65 @@
/*
* 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>
namespace ams::pinmux {
#define PINMUX_AUX_GEN1_I2C_SCL (0x30BC)
#define PINMUX_AUX_GEN1_I2C_SDA (0x30C0)
#define PINMUX_AUX_PWR_I2C_SCL (0x30DC)
#define PINMUX_AUX_PWR_I2C_SDA (0x30E0)
#define PINMUX_AUX_UART1_TX (0x30E4)
#define PINMUX_AUX_UART1_RX (0x30E8)
#define PINMUX_AUX_UART1_RTS (0x30EC)
#define PINMUX_AUX_UART1_CTS (0x30F0)
#define PINMUX_AUX_UART2_TX (0x30F4)
#define PINMUX_AUX_UART2_RX (0x30F8)
#define PINMUX_AUX_UART2_RTS (0x30FC)
#define PINMUX_AUX_UART2_CTS (0x3100)
#define PINMUX_AUX_UART3_TX (0x3104)
#define PINMUX_AUX_UART3_RX (0x3108)
#define PINMUX_AUX_UART3_RTS (0x310C)
#define PINMUX_AUX_UART3_CTS (0x3110)
#define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME)
#define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE)
#define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM)
#define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3);
}

View File

@ -58,6 +58,28 @@ namespace ams::uart {
}
}
constexpr inline u32 LockBit = (1 << 6);
void Lock(volatile UartRegisters *reg) {
while (true) {
if (reg->mie != 0) {
continue;
}
reg->irda_csr = LockBit;
if (reg->mie == 0) {
break;
}
reg->irda_csr = 0;
}
}
void Unlock(volatile UartRegisters *reg) {
reg->irda_csr = 0;
}
}
void SetRegisterAddress(uintptr_t address) {
@ -110,4 +132,36 @@ namespace ams::uart {
uart->spr = 0;
}
void SendText(Port port, const void *data, size_t size) {
/* Get the registers. */
auto *uart = GetRegisters(port);
/* Get pointer to data. */
const u8 *p = static_cast<const u8 *>(data);
/* Lock the uart registers. */
Lock(uart);
ON_SCOPE_EXIT { Unlock(uart); };
/* Send each byte. */
for (size_t i = 0; i < size; ++i) {
WaitFifoNotFull(uart);
if (p[i] == '\n') {
*reinterpret_cast<volatile u8 *>(std::addressof(uart->thr)) = '\r';
WaitFifoNotFull(uart);
}
*reinterpret_cast<volatile u8 *>(std::addressof(uart->thr)) = p[i];
}
}
void WaitFlush(Port port) {
/* Get the registers. */
auto *uart = GetRegisters(port);
/* Wait for idle. */
WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE);
}
}