From 3756f81d9e8d86917bf38bc5a55a8749ed1b7873 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 31 Oct 2020 00:47:45 -0700 Subject: [PATCH] wec: implement wec driver components for boot sysmodule --- .../wec/wec_wake_event.board.nintendo_nx.hpp | 2 + libstratosphere/source/wec/wec_api.cpp | 116 ++++++++++++++++++ .../include/vapours/dd/dd_io_mapping.hpp | 1 + libvapours/source/dd/dd_io_mapping.cpp | 65 +++++++++- 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 libstratosphere/source/wec/wec_api.cpp diff --git a/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp b/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp index afee3741..80012f9c 100644 --- a/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp +++ b/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp @@ -79,6 +79,8 @@ namespace ams::wec { WakeEvent_ModemWakeAp = 0x3D, WakeEvent_TouchInt = 0x3E, WakeEvent_MotionInt = 0x3F, + + WakeEvent_Count = 0x40, }; constexpr inline WakeEvent WakeEvent_None = static_cast(-1); diff --git a/libstratosphere/source/wec/wec_api.cpp b/libstratosphere/source/wec/wec_api.cpp new file mode 100644 index 00000000..3d65b264 --- /dev/null +++ b/libstratosphere/source/wec/wec_api.cpp @@ -0,0 +1,116 @@ +/* + * 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 + +/* TODO: How much of this should be namespaced under BOARD_NINTENDO_NX? */ + +namespace ams::wec { + + namespace { + + constexpr inline dd::PhysicalAddress ApbdevPmc = 0x7000E400; + + constinit bool g_initialized = false; + + void UpdateControlBit(dd::PhysicalAddress phys_addr, u32 mask, bool flag) { + dd::ReadModifyWriteIoRegister(phys_addr, flag ? ~0u : 0u, mask); + dd::ReadIoRegister(phys_addr); + } + + void Initialize(bool blink) { + /* Initialize WAKE_DEBOUNCE_EN. */ + dd::WriteIoRegister(ApbdevPmc + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0); + dd::ReadIoRegister(ApbdevPmc + APBDEV_PMC_WAKE_DEBOUNCE_EN); + + /* Initialize BLINK_TIMER. */ + dd::WriteIoRegister(ApbdevPmc + APBDEV_PMC_BLINK_TIMER, 0x08008800); + dd::ReadIoRegister(ApbdevPmc + APBDEV_PMC_BLINK_TIMER); + + /* Set control configs. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0800, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0400, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0200, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0100, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0040, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0020, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x4000, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x0200, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x0001, true); + + /* Update blink bit in APBDEV_PMC_CNTRL. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0080, blink); + + /* Update blink bit in APBDEV_PMC_DPD_PADS_ORIDE. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, blink); + } + + } + + /* 0C, 160 */ + /* 10, 164 */ + /* DC, 170 */ + + void Initialize() { + /* Set initial wake configuration. */ + if (!g_initialized) { + Initialize(false); + g_initialized = true; + } + } + + void ClearWakeEvents() { + /* TODO */ + AMS_ABORT(); + } + + void WecRestoreForExitSuspend() { + /* TODO */ + AMS_ABORT(); + } + + void SetWakeEventLevel(wec::WakeEvent event, wec::WakeEventLevel level) { + if (g_initialized && event < wec::WakeEvent_Count) { + /* Determine the event index. */ + const bool which = static_cast(event) < BITSIZEOF(u32); + const u32 index = static_cast(event) & (BITSIZEOF(u32) - 1); + + /* Get the level and auto_mask offsets. */ + u32 level_ofs = which ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL; + u32 auto_mask_ofs = which ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK; + + /* If the level isn't auto, swap the offsets. */ + if (level != wec::WakeEventLevel_Auto) { + std::swap(level_ofs, auto_mask_ofs); + } + + /* Clear the bit in the level register. */ + UpdateControlBit(ApbdevPmc + level_ofs, (1u << index), false); + + /* Set or clear the bit in the auto mask register. */ + UpdateControlBit(ApbdevPmc + level_ofs, (1u << index), level != wec::WakeEventLevel_Low); + } + } + + void SetWakeEventEnabled(wec::WakeEvent event, bool en) { + if (g_initialized && event < wec::WakeEvent_Count) { + /* Set or clear the relevant enabled bit. */ + const u32 offset = static_cast(event) < BITSIZEOF(u32) ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK; + const u32 index = static_cast(event) & (BITSIZEOF(u32) - 1); + UpdateControlBit(ApbdevPmc + offset, (1u << index), en); + } + } + +} diff --git a/libvapours/include/vapours/dd/dd_io_mapping.hpp b/libvapours/include/vapours/dd/dd_io_mapping.hpp index f82552bb..4a330377 100644 --- a/libvapours/include/vapours/dd/dd_io_mapping.hpp +++ b/libvapours/include/vapours/dd/dd_io_mapping.hpp @@ -22,5 +22,6 @@ namespace ams::dd { u32 ReadIoRegister(dd::PhysicalAddress phys_addr); void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value); + u32 ReadModifyWriteIoRegister(PhysicalAddress phys_addr, u32 value, u32 mask); } diff --git a/libvapours/source/dd/dd_io_mapping.cpp b/libvapours/source/dd/dd_io_mapping.cpp index 91358726..1bbdeb8c 100644 --- a/libvapours/source/dd/dd_io_mapping.cpp +++ b/libvapours/source/dd/dd_io_mapping.cpp @@ -70,13 +70,55 @@ namespace ams::dd { #endif } + namespace { + + #if defined(ATMOSPHERE_IS_STRATOSPHERE) + + #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + constexpr dd::PhysicalAddress PmcPhysStart = 0x7000E400; + constexpr dd::PhysicalAddress PmcPhysLast = 0x7000EFFF; + + constexpr bool IsValidPmcPhysicalAddress(dd::PhysicalAddress phys_addr) { + return util::IsAligned(phys_addr, alignof(u32)) && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysLast; + } + + u32 ReadWritePmcRegisterImpl(dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + u32 out_value; + R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::AtmosphereReadWriteRegister(phys_addr, mask, value, &out_value))); + return out_value; + } + + bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + if (IsValidPmcPhysicalAddress(phys_addr)) { + *out = ReadWritePmcRegisterImpl(phys_addr, value, mask); + return true; + } else { + return false; + } + } + + #else + + bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + AMS_UNUSED(out, phys_addr, value, mask); + return false; + } + + #endif + + #endif + } + u32 ReadIoRegister(dd::PhysicalAddress phys_addr) { #if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32))); #elif defined(ATMOSPHERE_IS_STRATOSPHERE) u32 val; - R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0)); + if (!TryReadModifyWritePmcRegister(std::addressof(val), phys_addr, 0, 0)) { + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0)); + } return val; #else @@ -90,7 +132,9 @@ namespace ams::dd { #elif defined(ATMOSPHERE_IS_STRATOSPHERE) u32 out_val; - R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, 0xFFFFFFFF, value)); + if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, 0xFFFFFFFF)) { + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, 0xFFFFFFFF, value)); + } AMS_UNUSED(out_val); #else @@ -98,4 +142,21 @@ namespace ams::dd { #endif } + u32 ReadModifyWriteIoRegister(PhysicalAddress phys_addr, u32 value, u32 mask) { + #if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) + AMS_UNUSED(phys_addr, value, mask); + AMS_ABORT("ReadModifyWriteIoRegister TODO under non-stratosphere"); + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + + u32 out_val; + if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, mask)) { + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, mask, value)); + } + return out_val; + + #else + #error "Unknown execution context for ams::dd::ReadModifyWriteIoRegister!" + #endif + } + }