diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 1ca25d4..2146502 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -108,15 +108,17 @@ DATA := data INCLUDES := include -GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1/os $1/cpu $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,)) +GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1/os $1/cpu $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(filter-out $d,$(call GENERAL_SOURCE_DIRS,$d)) $d,)) SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),)) UNFILTERED_SOURCE_DIRS=$1 $(foreach d,$(wildcard $1/*),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,)) -ALL_SOURCE_DIRS=$(call GENERAL_SOURCE_DIRS,$1) \ - $(call SPECIFIC_SOURCE_DIRS,$1,arch,$(ATMOSPHERE_ARCH_DIR)) \ - $(call SPECIFIC_SOURCE_DIRS,$1,board,$(ATMOSPHERE_BOARD_DIR)) \ - $(call SPECIFIC_SOURCE_DIRS,$1,os,$(ATMOSPHERE_OS_DIR)) \ - $(call SPECIFIC_SOURCE_DIRS,$1,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_DIR)) +ALL_SOURCE_DIRS=$(foreach d,$(call GENERAL_SOURCE_DIRS,$1), \ + $d \ + $(call SPECIFIC_SOURCE_DIRS,$d,arch,$(ATMOSPHERE_ARCH_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$d,board,$(ATMOSPHERE_BOARD_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$d,os,$(ATMOSPHERE_OS_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$d,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_DIR)) \ + ) SOURCES ?= $(call ALL_SOURCE_DIRS,source) diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo/nx/gpio_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo/nx/gpio_driver_api.hpp new file mode 100644 index 0000000..4245007 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo/nx/gpio_driver_api.hpp @@ -0,0 +1,28 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver::board::nintendo_nx { + + void Initialize(bool enable_interrupt_handlers); + + void SetInitialGpioConfig(); + void SetInitialWakePinConfig(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp deleted file mode 100644 index 4245007..0000000 --- a/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include -#include - -namespace ams::gpio::driver::board::nintendo_nx { - - void Initialize(bool enable_interrupt_handlers); - - void SetInitialGpioConfig(); - void SetInitialWakePinConfig(); - -} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp index 6025716..9f42097 100644 --- a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp @@ -22,7 +22,7 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include + #include namespace ams::gpio::driver::board { diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo/nx/i2c_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo/nx/i2c_driver_api.hpp new file mode 100644 index 0000000..67bf082 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo/nx/i2c_driver_api.hpp @@ -0,0 +1,24 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::i2c::driver::board::nintendo_nx { + + void Initialize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp deleted file mode 100644 index 67bf082..0000000 --- a/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include - -namespace ams::i2c::driver::board::nintendo_nx { - - void Initialize(); - -} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp index ce5f587..c9152ae 100644 --- a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp @@ -21,7 +21,7 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include + #include namespace ams::i2c::driver::board { diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo/nx/pwm_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo/nx/pwm_driver_api.hpp new file mode 100644 index 0000000..0a9b003 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo/nx/pwm_driver_api.hpp @@ -0,0 +1,24 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::pwm::driver::board::nintendo_nx { + + void Initialize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp deleted file mode 100644 index 0a9b003..0000000 --- a/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include - -namespace ams::pwm::driver::board::nintendo_nx { - - void Initialize(); - -} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp index c9fbec5..4499240 100644 --- a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp @@ -20,7 +20,7 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include + #include namespace ams::pwm::driver::board { diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp new file mode 100644 index 0000000..513add6 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp @@ -0,0 +1,88 @@ +/* + * 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 "impl/gpio_driver_impl.hpp" +#include "impl/gpio_initial_config.hpp" +#include "impl/gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx { + + namespace { + + ams::gpio::driver::board::nintendo_nx::impl::DriverImpl *g_driver_impl = nullptr; + + } + + void Initialize(bool enable_interrupt_handlers) { + /* Check that we haven't previously initialized. */ + AMS_ABORT_UNLESS(g_driver_impl == nullptr); + + /* Get the device driver subsystem framework memory resource. */ + auto *memory_resource = ddsf::GetMemoryResource(); + + /* Allocate a new driver. */ + auto *driver_storage = static_cast(memory_resource->Allocate(sizeof(*g_driver_impl))); + AMS_ABORT_UNLESS(driver_storage != nullptr); + + /* Construct the new driver. */ + g_driver_impl = new (driver_storage) ams::gpio::driver::board::nintendo_nx::impl::DriverImpl(impl::GpioRegistersPhysicalAddress, impl::GpioRegistersSize); + + /* Register the driver. */ + gpio::driver::RegisterDriver(g_driver_impl); + + /* Register interrupt handlers, if we should. */ + if (enable_interrupt_handlers) { + for (size_t i = 0; i < util::size(impl::InterruptNameTable); ++i) { + /* Allocate a handler. */ + impl::InterruptEventHandler *handler_storage = static_cast(memory_resource->Allocate(sizeof(impl::InterruptEventHandler))); + AMS_ABORT_UNLESS(handler_storage != nullptr); + + /* Initialize the handler. */ + impl::InterruptEventHandler *handler = new (handler_storage) impl::InterruptEventHandler; + handler->Initialize(g_driver_impl, impl::InterruptNameTable[i], static_cast(i)); + + /* Register the handler. */ + gpio::driver::RegisterInterruptHandler(handler); + } + } + + /* Create and register all pads. */ + for (const auto &entry : impl::PadMapCombinationList) { + /* Allocate a pad for our device. */ + impl::TegraPad *pad_storage = static_cast(memory_resource->Allocate(sizeof(impl::TegraPad))); + AMS_ABORT_UNLESS(pad_storage != nullptr); + + /* Create a pad for our device. */ + impl::TegraPad *pad = new (pad_storage) impl::TegraPad; + pad->SetParameters(entry.internal_number, impl::PadInfo{entry.wake_event}); + + /* Register the pad with our driver. */ + g_driver_impl->RegisterDevice(pad); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(gpio::driver::RegisterDeviceCode(entry.device_code, pad)); + } + } + + void SetInitialGpioConfig() { + return impl::SetInitialGpioConfig(); + } + + void SetInitialWakePinConfig() { + return impl::SetInitialWakePinConfig(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.cpp new file mode 100644 index 0000000..aa152fb --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.cpp @@ -0,0 +1,342 @@ +/* + * 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 "gpio_driver_impl.hpp" +#include "gpio_register_accessor.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + void InterruptEventHandler::Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr) { + /* Set fields. */ + this->driver = drv; + this->interrupt_name = intr; + this->controller_number = ctlr; + + /* Initialize interrupt event. */ + os::InitializeInterruptEvent(std::addressof(this->interrupt_event), intr, os::EventClearMode_ManualClear); + + /* Initialize base. */ + IEventHandler::Initialize(std::addressof(this->interrupt_event)); + } + + void InterruptEventHandler::HandleEvent() { + /* Lock the driver's interrupt mutex. */ + std::scoped_lock lk(this->driver->interrupt_control_mutex); + + /* Check each pad. */ + bool found = false; + for (auto it = this->driver->interrupt_pad_list.begin(); !found && it != this->driver->interrupt_pad_list.end(); ++it) { + found = this->CheckAndHandleInterrupt(*it); + } + + /* If we didn't find a pad, clear the interrupt event. */ + if (!found) { + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + } + } + + bool InterruptEventHandler::CheckAndHandleInterrupt(TegraPad &pad) { + /* Get the pad's number. */ + const InternalGpioPadNumber pad_number = static_cast(pad.GetPadNumber()); + + /* Check if the pad matches our controller number. */ + if (this->controller_number != ConvertInternalGpioPadNumberToController(pad_number)) { + return false; + } + + /* Get the addresses of INT_STA, INT_ENB. */ + const uintptr_t sta_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); + const uintptr_t enb_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + /* Check if both STA and ENB are set. */ + if (reg::Read(sta_address, 1u << pad_index) == 0 || reg::Read(enb_address, 1u << pad_index) == 0) { + return false; + } + + /* The pad is signaled. First, clear the enb bit. */ + SetMaskedBit(enb_address, pad_index, 0); + reg::Read(enb_address); + + /* Disable the interrupt on the pad. */ + pad.SetInterruptEnabled(false); + this->driver->RemoveInterruptPad(std::addressof(pad)); + + /* Clear the interrupt event. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + + /* Signal the pad's bound event. */ + pad.SignalInterruptBoundEvent(); + + return true; + } + + DriverImpl::DriverImpl(dd::PhysicalAddress reg_paddr, size_t size) : gpio_physical_address(reg_paddr), gpio_virtual_address(), suspend_handler(this), interrupt_pad_list(), interrupt_control_mutex() { + /* Get the corresponding virtual address for our physical address. */ + this->gpio_virtual_address = dd::QueryIoMapping(reg_paddr, size); + AMS_ABORT_UNLESS(this->gpio_virtual_address != 0); + } + + void DriverImpl::InitializeDriver() { + /* Initialize our suspend handler. */ + this->suspend_handler.Initialize(this->gpio_virtual_address); + } + + void DriverImpl::FinalizeDriver() { + /* ... */ + } + + Result DriverImpl::InitializePad(Pad *pad) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad as GPIO by modifying the appropriate bit in CNF. */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_CNF, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, 1); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + void DriverImpl::FinalizePad(Pad *pad) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Nothing to do. */ + AMS_UNUSED(pad); + } + + Result DriverImpl::GetDirection(Direction *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad direction by reading the appropriate bit in OE */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + if (reg::Read(pad_address, 1u << pad_index) != 0) { + *out = Direction_Output; + } else { + *out = Direction_Input; + } + + return ResultSuccess(); + + } + + Result DriverImpl::SetDirection(Pad *pad, Direction direction) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad direction by modifying the appropriate bit in OE */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, direction); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::GetValue(GpioValue *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad value by reading the appropriate bit in IN */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + if (reg::Read(pad_address, 1u << pad_index) != 0) { + *out = GpioValue_High; + } else { + *out = GpioValue_Low; + } + + return ResultSuccess(); + } + + Result DriverImpl::SetValue(Pad *pad, GpioValue value) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad value by modifying the appropriate bit in IN */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, value); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::GetInterruptMode(InterruptMode *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad mode by reading the appropriate bits in INT_LVL */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + switch ((reg::Read(pad_address) >> pad_index) & InternalInterruptMode_Mask) { + case InternalInterruptMode_LowLevel: *out = InterruptMode_LowLevel; break; + case InternalInterruptMode_HighLevel: *out = InterruptMode_HighLevel; break; + case InternalInterruptMode_RisingEdge: *out = InterruptMode_RisingEdge; break; + case InternalInterruptMode_FallingEdge: *out = InterruptMode_FallingEdge; break; + case InternalInterruptMode_AnyEdge: *out = InterruptMode_AnyEdge; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result DriverImpl::SetInterruptMode(Pad *pad, InterruptMode mode) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad mode by modifying the appropriate bits in INT_LVL */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + switch (mode) { + case InterruptMode_LowLevel: reg::ReadWrite(pad_address, InternalInterruptMode_LowLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_HighLevel: reg::ReadWrite(pad_address, InternalInterruptMode_HighLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_RisingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_RisingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_FallingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_FallingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_AnyEdge: reg::ReadWrite(pad_address, InternalInterruptMode_AnyEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::SetInterruptEnabled(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetInterruptStatus(InterruptStatus *out, Pad *pad) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::ClearInterruptStatus(Pad *pad) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetDebounceEnabled(bool *out, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetDebounceEnabled(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetDebounceTime(s32 *out_ms, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetDebounceTime(Pad *pad, s32 ms) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetUnknown22(u32 *out) { + /* TODO */ + AMS_ABORT(); + } + + void DriverImpl::Unknown23() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetValueForSleepState(Pad *pad, GpioValue value) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::IsWakeEventActive(bool *out, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetWakePinDebugMode(WakePinDebugMode mode) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::Suspend() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SuspendLow() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::Resume() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::ResumeLow() { + /* TODO */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.hpp new file mode 100644 index 0000000..b78f341 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_driver_impl.hpp @@ -0,0 +1,129 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" +#include "gpio_register_accessor.hpp" +#include "gpio_suspend_handler.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + class DriverImpl; + + class InterruptEventHandler : public ddsf::IEventHandler { + private: + DriverImpl *driver; + os::InterruptName interrupt_name; + os::InterruptEventType interrupt_event; + int controller_number; + private: + bool CheckAndHandleInterrupt(TegraPad &pad); + public: + InterruptEventHandler() : IEventHandler(), driver(nullptr), interrupt_name(), interrupt_event(), controller_number() { /* ... */ } + + void Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr); + + virtual void HandleEvent() override; + }; + + class DriverImpl : public ::ams::gpio::driver::IGpioDriver { + NON_COPYABLE(DriverImpl); + NON_MOVEABLE(DriverImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::DriverImpl, ::ams::gpio::driver::IGpioDriver); + friend class InterruptEventHandler; + private: + dd::PhysicalAddress gpio_physical_address; + uintptr_t gpio_virtual_address; + SuspendHandler suspend_handler; + TegraPad::InterruptList interrupt_pad_list; + mutable os::SdkMutex interrupt_control_mutex; + public: + DriverImpl(dd::PhysicalAddress reg_paddr, size_t size); + + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializePad(Pad *pad) override; + virtual void FinalizePad(Pad *pad) override; + + virtual Result GetDirection(Direction *out, Pad *pad) const override; + virtual Result SetDirection(Pad *pad, Direction direction) override; + + virtual Result GetValue(GpioValue *out, Pad *pad) const override; + virtual Result SetValue(Pad *pad, GpioValue value) override; + + virtual Result GetInterruptMode(InterruptMode *out, Pad *pad) const override; + virtual Result SetInterruptMode(Pad *pad, InterruptMode mode) override; + + virtual Result SetInterruptEnabled(Pad *pad, bool en) override; + + virtual Result GetInterruptStatus(InterruptStatus *out, Pad *pad) override; + virtual Result ClearInterruptStatus(Pad *pad) override; + + virtual os::SdkMutex &GetInterruptControlMutex(const Pad &pad) const override { + AMS_UNUSED(pad); + return this->interrupt_control_mutex; + } + + virtual Result GetDebounceEnabled(bool *out, Pad *pad) const override; + virtual Result SetDebounceEnabled(Pad *pad, bool en) override; + + virtual Result GetDebounceTime(s32 *out_ms, Pad *pad) const override; + virtual Result SetDebounceTime(Pad *pad, s32 ms) override; + + virtual Result GetUnknown22(u32 *out) override; + virtual void Unknown23() override; + + virtual Result SetValueForSleepState(Pad *pad, GpioValue value) override; + virtual Result IsWakeEventActive(bool *out, Pad *pad) const override; + virtual Result SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) override; + virtual Result SetWakePinDebugMode(WakePinDebugMode mode) override; + + virtual Result Suspend() override; + virtual Result SuspendLow() override; + virtual Result Resume() override; + virtual Result ResumeLow() override; + private: + static constexpr ALWAYS_INLINE TegraPad &GetTegraPad(Pad *pad) { + AMS_ASSERT(pad != nullptr); + return static_cast(*pad); + } + + static ALWAYS_INLINE const PadInfo &GetInfo(Pad *pad) { + return GetTegraPad(pad).GetInfo(); + } + + static ALWAYS_INLINE PadStatus &GetStatus(Pad *pad) { + return GetTegraPad(pad).GetStatus(); + } + + void AddInterruptPad(TegraPad *pad) { + AMS_ASSERT(pad != nullptr); + if (!pad->IsLinkedToInterruptBoundPadList()) { + this->interrupt_pad_list.push_back(*pad); + } + } + + void RemoveInterruptPad(TegraPad *pad) { + AMS_ASSERT(pad != nullptr); + if (pad->IsLinkedToInterruptBoundPadList()) { + this->interrupt_pad_list.erase(this->interrupt_pad_list.iterator_to(*pad)); + } + } + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.cpp new file mode 100644 index 0000000..b6b1022 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.cpp @@ -0,0 +1,172 @@ +/* + * 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 "gpio_driver_impl.hpp" +#include "gpio_initial_config.hpp" +#include "gpio_wake_pin_config.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + namespace { + + spl::HardwareType GetHardwareType() { + /* Acquire access to spl: */ + sm::ScopedServiceHolder spl_holder; + AMS_ABORT_UNLESS(static_cast(spl_holder)); + + /* Get config. */ + return ::ams::spl::GetHardwareType(); + } + + #include "gpio_initial_wake_pin_config_icosa.inc" + /* #include "gpio_initial_wake_pin_config_copper.inc" */ + #include "gpio_initial_wake_pin_config_hoag.inc" + #include "gpio_initial_wake_pin_config_iowa.inc" + #include "gpio_initial_wake_pin_config_calcio.inc" + #include "gpio_initial_wake_pin_config_aula.inc" + + #include "gpio_initial_config_icosa.inc" + /* #include "gpio_initial_config_copper.inc" */ + #include "gpio_initial_config_hoag.inc" + #include "gpio_initial_config_iowa.inc" + #include "gpio_initial_config_calcio.inc" + #include "gpio_initial_config_aula.inc" + + } + + void SetInitialGpioConfig() { + /* Set wake event levels, wake event enables. */ + const GpioInitialConfig *configs = nullptr; + size_t num_configs = 0; + + /* Select the correct config. */ + switch (GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = InitialGpioConfigsIcosa; + num_configs = NumInitialGpioConfigsIcosa; + break; + case spl::HardwareType::Hoag: + configs = InitialGpioConfigsHoag; + num_configs = NumInitialGpioConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = InitialGpioConfigsIowa; + num_configs = NumInitialGpioConfigsIowa; + break; + case spl::HardwareType::Calcio: + configs = InitialGpioConfigsCalcio; + num_configs = NumInitialGpioConfigsCalcio; + break; + case spl::HardwareType::Aula: + configs = InitialGpioConfigsAula; + num_configs = NumInitialGpioConfigsAula; + break; + case spl::HardwareType::Copper: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check we can use our config. */ + AMS_ABORT_UNLESS(configs != nullptr); + + /* Apply the configs. */ + { + /* Create a driver to use for the duration of our application. */ + DriverImpl driver(GpioRegistersPhysicalAddress, GpioRegistersSize); + driver.InitializeDriver(); + + for (size_t i = 0; i < num_configs; ++i) { + /* Find the internal pad number for our device. */ + bool found = false; + for (const auto &entry : PadMapCombinationList) { + if (entry.device_code == configs[i].device_code) { + /* We found an entry. */ + found = true; + + /* Create a pad for our device. */ + TegraPad pad; + pad.SetParameters(entry.internal_number, PadInfo{entry.wake_event}); + + /* Initialize the pad. */ + R_ABORT_UNLESS(driver.InitializePad(std::addressof(pad))); + + /* Set the direction. */ + R_ABORT_UNLESS(driver.SetDirection(std::addressof(pad), configs[i].direction)); + + /* If the direction is output, set the value. */ + if (configs[i].direction == Direction_Output) { + R_ABORT_UNLESS(driver.SetValue(std::addressof(pad), configs[i].value)); + } + + /* Finalize the pad we made. */ + driver.FinalizePad(std::addressof(pad)); + break; + } + } + + /* Ensure that we applied the config for the pad we wanted. */ + AMS_ABORT_UNLESS(found); + } + + /* Finalize the driver. */ + driver.FinalizeDriver(); + } + } + + void SetInitialWakePinConfig() { + /* Ensure the wec driver is initialized. */ + ams::wec::Initialize(); + + /* Set wake event levels, wake event enables. */ + const WakePinConfig *configs = nullptr; + size_t num_configs = 0; + + /* Select the correct config. */ + switch (GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = InitialWakePinConfigsIcosa; + num_configs = NumInitialWakePinConfigsIcosa; + break; + case spl::HardwareType::Hoag: + configs = InitialWakePinConfigsHoag; + num_configs = NumInitialWakePinConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = InitialWakePinConfigsIowa; + num_configs = NumInitialWakePinConfigsIowa; + break; + case spl::HardwareType::Calcio: + configs = InitialWakePinConfigsCalcio; + num_configs = NumInitialWakePinConfigsCalcio; + break; + case spl::HardwareType::Aula: + configs = InitialWakePinConfigsAula; + num_configs = NumInitialWakePinConfigsAula; + break; + case spl::HardwareType::Copper: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check we can use our config. */ + AMS_ABORT_UNLESS(configs != nullptr); + + /* Apply the config. */ + for (size_t i = 0; i < num_configs; ++i) { + wec::SetWakeEventLevel(configs[i].wake_event, configs[i].level); + wec::SetWakeEventEnabled(configs[i].wake_event, configs[i].enable); + } + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.hpp new file mode 100644 index 0000000..d577ccc --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config.hpp @@ -0,0 +1,30 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + struct GpioInitialConfig { + DeviceCode device_code; + gpio::Direction direction; + gpio::GpioValue value; + }; + + void SetInitialGpioConfig(); + void SetInitialWakePinConfig(); + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_aula.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_aula.inc new file mode 100644 index 0000000..e1c90ee --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_aula.inc @@ -0,0 +1,70 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsAula[] = { + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortH0, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsAula = util::size(InitialGpioConfigsAula); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_calcio.inc new file mode 100644 index 0000000..e20acc0 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_calcio.inc @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsCalcio[] = { + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_GpioPortF1, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_Hdmi5VEn, Direction_Output, GpioValue_Low }, + { DeviceCode_UsbSwitchB1Oc, Direction_Input, GpioValue_High }, + { DeviceCode_HdmiPdTrEn, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_UsbSwitchB1En, Direction_Output, GpioValue_Low }, + { DeviceCode_HdmiHpd, Direction_Input, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsCalcio = util::size(InitialGpioConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_hoag.inc new file mode 100644 index 0000000..97a45b2 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_hoag.inc @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsHoag[] = { + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_McuIrq, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_McuBoot, Direction_Output, GpioValue_Low }, + { DeviceCode_McuRst, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdGpio1, Direction_Input, GpioValue_High }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_NfcEn, Direction_Output, GpioValue_Low }, + { DeviceCode_NfcIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_NfcRst, Direction_Output, GpioValue_Low }, + { DeviceCode_GpioPortC7, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortD0, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortC5, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortC6, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortY7, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_McuPor, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsHoag = util::size(InitialGpioConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_icosa.inc new file mode 100644 index 0000000..85800f5 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_icosa.inc @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsIcosa[] = { + { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowGc, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_High }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_High }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_High }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_Max77621GpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsIcosa = util::size(InitialGpioConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_iowa.inc new file mode 100644 index 0000000..1b71bd7 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_config_iowa.inc @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsIowa[] = { + { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsIowa = util::size(InitialGpioConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_aula.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_aula.inc new file mode 100644 index 0000000..d955382 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_aula.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsAula[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsAula = util::size(InitialWakePinConfigsAula); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_calcio.inc new file mode 100644 index 0000000..6cdd662 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_calcio.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsCalcio[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsCalcio = util::size(InitialWakePinConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_hoag.inc new file mode 100644 index 0000000..177fcc5 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_hoag.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsHoag[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsHoag = util::size(InitialWakePinConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_icosa.inc new file mode 100644 index 0000000..19c40e6 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_icosa.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsIcosa[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsIcosa = util::size(InitialWakePinConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_iowa.inc new file mode 100644 index 0000000..ebd8016 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_initial_wake_pin_config_iowa.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsIowa[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsIowa = util::size(InitialWakePinConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_internal_pad_map_combination.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_internal_pad_map_combination.inc new file mode 100644 index 0000000..a9ff372 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_internal_pad_map_combination.inc @@ -0,0 +1,112 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const PadMapCombination PadMapCombinationList[] = { + { DeviceCode_CodecLdoEnTemp, InternalGpioPadNumber_Port_Z_4, ams::wec::WakeEvent_None }, + { DeviceCode_PowSdEn, InternalGpioPadNumber_Port_E_4, ams::wec::WakeEvent_None }, + { DeviceCode_BtRst, InternalGpioPadNumber_Port_H_4, ams::wec::WakeEvent_None }, + { DeviceCode_RamCode3, InternalGpioPadNumber_Port_BB_2, ams::wec::WakeEvent_None }, + { DeviceCode_GameCardReset, InternalGpioPadNumber_Port_BB_3, ams::wec::WakeEvent_None }, + { DeviceCode_CodecAlert, InternalGpioPadNumber_Port_BB_4, ams::wec::WakeEvent_None }, + { DeviceCode_PowGc, InternalGpioPadNumber_Port_E_5, ams::wec::WakeEvent_None }, + { DeviceCode_DebugControllerDet, InternalGpioPadNumber_Port_S_0, ams::wec::WakeEvent_None }, + { DeviceCode_BattChgStatus, InternalGpioPadNumber_Port_S_1, ams::wec::WakeEvent_None }, + { DeviceCode_BattChgEnableN, InternalGpioPadNumber_Port_S_6, ams::wec::WakeEvent_None }, + { DeviceCode_FanTach, InternalGpioPadNumber_Port_S_7, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd50AEn, InternalGpioPadNumber_Port_A_5, ams::wec::WakeEvent_None }, + { DeviceCode_SdevCoaxSel1, InternalGpioPadNumber_Port_P_0, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType0, InternalGpioPadNumber_Port_P_5, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType1, InternalGpioPadNumber_Port_P_4, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType2, InternalGpioPadNumber_Port_P_3, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType3, InternalGpioPadNumber_Port_P_2, ams::wec::WakeEvent_None }, + { DeviceCode_TempAlert, InternalGpioPadNumber_Port_X_4, ams::wec::WakeEvent_None }, + { DeviceCode_CodecHpDetIrq, InternalGpioPadNumber_Port_V_6, ams::wec::WakeEvent_None }, + { DeviceCode_MotionInt, InternalGpioPadNumber_Port_X_2, ams::wec::WakeEvent_None }, + { DeviceCode_TpIrq, InternalGpioPadNumber_Port_X_1, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonSleep2, InternalGpioPadNumber_Port_X_5, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonVolUp, InternalGpioPadNumber_Port_X_6, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonVolDn, InternalGpioPadNumber_Port_X_7, ams::wec::WakeEvent_None }, + { DeviceCode_RecoveryKey, InternalGpioPadNumber_Port_Y_1, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdBlEn, InternalGpioPadNumber_Port_V_1, ams::wec::WakeEvent_None }, + { DeviceCode_LcdReset, InternalGpioPadNumber_Port_V_2, ams::wec::WakeEvent_None }, + { DeviceCode_PdVconnEn, InternalGpioPadNumber_Port_K_5, ams::wec::WakeEvent_None }, + { DeviceCode_PdRstN, InternalGpioPadNumber_Port_V_5, ams::wec::WakeEvent_None }, + { DeviceCode_SdevCoaxSel0, InternalGpioPadNumber_Port_Z_2, ams::wec::WakeEvent_None }, + { DeviceCode_SdWp, InternalGpioPadNumber_Port_Z_3, ams::wec::WakeEvent_None }, + { DeviceCode_TpReset, InternalGpioPadNumber_Port_J_7, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio2, InternalGpioPadNumber_Port_K_0, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio3, InternalGpioPadNumber_Port_K_1, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio4, InternalGpioPadNumber_Port_K_2, ams::wec::WakeEvent_None }, + { DeviceCode_PowVcpuInt, InternalGpioPadNumber_Port_K_6, ams::wec::WakeEvent_None }, + { DeviceCode_Max77621GpuInt, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconChgU, InternalGpioPadNumber_Port_K_3, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconChgS, InternalGpioPadNumber_Port_CC_3, ams::wec::WakeEvent_None }, + { DeviceCode_WifiRfDisable, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, + { DeviceCode_WifiReset, InternalGpioPadNumber_Port_H_1, ams::wec::WakeEvent_None }, + { DeviceCode_ApWakeBt, InternalGpioPadNumber_Port_H_3, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio5, InternalGpioPadNumber_Port_H_7, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdVddPEn, InternalGpioPadNumber_Port_I_0, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdVddNEn, InternalGpioPadNumber_Port_I_1, ams::wec::WakeEvent_None }, + { DeviceCode_RamCode2, InternalGpioPadNumber_Port_CC_2, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd50BEn, InternalGpioPadNumber_Port_CC_4, ams::wec::WakeEvent_None }, + { DeviceCode_OtgFet1ForSdev, InternalGpioPadNumber_Port_J_5, ams::wec::WakeEvent_None }, + { DeviceCode_OtgFet2ForSdev, InternalGpioPadNumber_Port_L_0, ams::wec::WakeEvent_None }, + { DeviceCode_ExtConWakeU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_None }, + { DeviceCode_ExtConWakeS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_None }, + { DeviceCode_ExtUart2Rts, InternalGpioPadNumber_Port_G_2, ams::wec::WakeEvent_None }, + { DeviceCode_ExtUart3Rts, InternalGpioPadNumber_Port_D_3, ams::wec::WakeEvent_None }, + { DeviceCode_Debug0, InternalGpioPadNumber_Port_E_0, ams::wec::WakeEvent_None }, + { DeviceCode_Debug1, InternalGpioPadNumber_Port_E_1, ams::wec::WakeEvent_None }, + { DeviceCode_Debug2, InternalGpioPadNumber_Port_E_2, ams::wec::WakeEvent_None }, + { DeviceCode_Debug3, InternalGpioPadNumber_Port_E_3, ams::wec::WakeEvent_None }, + { DeviceCode_NfcIrq, InternalGpioPadNumber_Port_J_4, ams::wec::WakeEvent_None }, + { DeviceCode_NfcRst, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, + { DeviceCode_McuIrq, InternalGpioPadNumber_Port_E_7, ams::wec::WakeEvent_McuIrq }, + { DeviceCode_McuBoot, InternalGpioPadNumber_Port_T_0, ams::wec::WakeEvent_None }, + { DeviceCode_McuRst, InternalGpioPadNumber_Port_T_1, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd5V3En, InternalGpioPadNumber_Port_X_3, ams::wec::WakeEvent_None }, + { DeviceCode_McuPor, InternalGpioPadNumber_Port_CC_5, ams::wec::WakeEvent_None }, + { DeviceCode_NfcEn, InternalGpioPadNumber_Port_J_6, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC7, InternalGpioPadNumber_Port_C_7, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortD0, InternalGpioPadNumber_Port_D_0, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC5, InternalGpioPadNumber_Port_C_5, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC6, InternalGpioPadNumber_Port_C_6, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortY7, InternalGpioPadNumber_Port_Y_5, ams::wec::WakeEvent_None }, + { DeviceCode_Hdmi5VEn, InternalGpioPadNumber_Port_C_0, ams::wec::WakeEvent_None }, + { DeviceCode_UsbSwitchB1En, InternalGpioPadNumber_Port_C_1, ams::wec::WakeEvent_None }, + { DeviceCode_HdmiPdTrEn, InternalGpioPadNumber_Port_C_2, ams::wec::WakeEvent_None }, + { DeviceCode_UsbSwitchB1Oc, InternalGpioPadNumber_Port_CC_6, ams::wec::WakeEvent_None }, + { DeviceCode_HdmiHpd, InternalGpioPadNumber_Port_CC_1, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortF1, InternalGpioPadNumber_Port_F_1, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortH0, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconDetS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_ExtconDetS }, + { DeviceCode_GameCardCd, InternalGpioPadNumber_Port_S_3, ams::wec::WakeEvent_CamI2cSda }, + { DeviceCode_BattMgicIrq, InternalGpioPadNumber_Port_Y_0, ams::wec::WakeEvent_ButtonSlideSw }, + { DeviceCode_Bq24190Irq, InternalGpioPadNumber_Port_Z_0, ams::wec::WakeEvent_Bq24190Irq }, + { DeviceCode_CradleIrq, InternalGpioPadNumber_Port_K_4, ams::wec::WakeEvent_CradleIrq }, + { DeviceCode_BtWakeAp, InternalGpioPadNumber_Port_H_5, ams::wec::WakeEvent_BtWakeAp }, + { DeviceCode_ExtconDetU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_ExtconDetU }, + { DeviceCode_WifiWakeHost, InternalGpioPadNumber_Port_H_2, ams::wec::WakeEvent_WifiWakeAp }, + { DeviceCode_SdCd, InternalGpioPadNumber_Port_Z_1, ams::wec::WakeEvent_SdCd }, + { DeviceCode_ExtUart2Cts, InternalGpioPadNumber_Port_G_3, ams::wec::WakeEvent_Uart2Cts }, + { DeviceCode_ExtUart3Cts, InternalGpioPadNumber_Port_D_4, ams::wec::WakeEvent_Uart3Cts }, + { DeviceCode_LcdGpio1, InternalGpioPadNumber_Port_V_3, ams::wec::WakeEvent_LcdGpio1 }, + { DeviceCode_PmuIrq, InternalGpioPadNumber_None, ams::wec::WakeEvent_PwrIntN }, +}; + +constexpr inline size_t PadMapCombinationListSize = util::size(PadMapCombinationList); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_register_accessor.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_register_accessor.hpp new file mode 100644 index 0000000..c9b2f6e --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_register_accessor.hpp @@ -0,0 +1,135 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000D000; + constexpr inline size_t GpioRegistersSize = 4_KB; + + constexpr inline int GpioPerControllerBitWidth = 5; + constexpr inline int GpioControllerBitWidth = 3; + + constexpr inline int PortPerControllerBitWidth = 2; + constexpr inline int PortPerController = (1 << PortPerControllerBitWidth); + + constexpr inline int GpioPerPortBitWidth = 3; + constexpr inline int GpioPerPort = (1 << GpioPerPortBitWidth); + + static_assert(PortPerControllerBitWidth + GpioPerPortBitWidth == GpioPerControllerBitWidth); + static_assert(PortPerController * GpioPerPort == (1 << GpioPerControllerBitWidth)); + + constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { + return (number >> GpioPerControllerBitWidth); + } + + constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { + return (number >> GpioControllerBitWidth); + } + + constexpr InternalGpioPadNumber ConvertPortToInternalGpioPadNumber(int port) { + return static_cast(port << GpioControllerBitWidth); + } + + constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { + return (number & (GpioPerPort - 1)); + } + + constexpr int ConvertPortNumberToOffset(int port_number) { + return (port_number & (PortPerController - 1)); + } + + enum GpioController { + GpioController_1 = 0, + GpioController_2 = 1, + GpioController_3 = 2, + GpioController_4 = 3, + GpioController_5 = 4, + GpioController_6 = 5, + GpioController_7 = 6, + GpioController_8 = 7, + GpioController_Count = 8, + }; + static_assert(GpioController_Count == static_cast(1 << GpioControllerBitWidth)); + + constexpr inline os::InterruptName InterruptNameTable[GpioController_Count] = { + 64, /* GpioController_1 */ + 65, /* GpioController_2 */ + 66, /* GpioController_3 */ + 67, /* GpioController_4 */ + 87, /* GpioController_5 */ + 119, /* GpioController_6 */ + 121, /* GpioController_7 */ + 157, /* GpioController_8 */ + }; + + enum InternalInterruptMode { + InternalInterruptMode_LowLevel = 0x000000, + InternalInterruptMode_HighLevel = 0x000001, + InternalInterruptMode_RisingEdge = 0x000101, + InternalInterruptMode_FallingEdge = 0x000100, + InternalInterruptMode_AnyEdge = 0x010100, + + InternalInterruptMode_Mask = 0x010101, + }; + + enum GpioRegisterType { + GpioRegisterType_GPIO_CNF = 0, + GpioRegisterType_GPIO_OE = 1, + GpioRegisterType_GPIO_OUT = 2, + GpioRegisterType_GPIO_IN = 3, + GpioRegisterType_GPIO_INT_STA = 4, + GpioRegisterType_GPIO_INT_ENB = 5, + GpioRegisterType_GPIO_INT_LVL = 6, + GpioRegisterType_GPIO_INT_CLR = 7, + GpioRegisterType_GPIO_DB_CTRL = 8, + GpioRegisterType_GPIO_DB_CNT = 9, + }; + + constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; + constexpr inline int MaskedWriteBitOffset = 8; + + constexpr inline uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { + const auto controller = ConvertInternalGpioPadNumberToController(pad_number); + const auto port = ConvertInternalGpioPadNumberToPort(pad_number); + const auto offset = ConvertPortNumberToOffset(port); + + switch (reg_type) { + default: + return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CTRL: + return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CNT: + return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); + } + } + + inline void SetMaskedBit(uintptr_t pad_address, int index, int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); + } + + inline void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.cpp new file mode 100644 index 0000000..a922b21 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.cpp @@ -0,0 +1,69 @@ +/* + * 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 "gpio_suspend_handler.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + void SuspendHandler::Initialize(uintptr_t gpio_vaddr) { + /* Set our gpio virtual address. */ + this->gpio_virtual_address = gpio_vaddr; + + /* Ensure that we can use the wec library. */ + ams::wec::Initialize(); + } + + void SuspendHandler::SetValueForSleepState(TegraPad *pad, GpioValue value) { + /* TODO */ + AMS_ABORT(); + } + + Result SuspendHandler::IsWakeEventActive(bool *out, TegraPad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result SuspendHandler::SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::SetWakePinDebugMode(WakePinDebugMode mode) { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::Suspend() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::SuspendLow() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::Resume() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::ResumeLow() { + /* TODO */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.hpp new file mode 100644 index 0000000..faaf59b --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_suspend_handler.hpp @@ -0,0 +1,89 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + class SuspendHandler { + NON_COPYABLE(SuspendHandler); + NON_MOVEABLE(SuspendHandler); + private: + struct RegisterValues { + u16 conf; + u8 oe; + u8 out; + u8 int_enb; + u32 int_lvl; + u8 db_ctrl; + u8 db_cnt; + + void Reset() { + this->conf = 0; + this->oe = 0; + this->out = 0; + this->int_enb = 0; + this->int_lvl = 0; + this->db_ctrl = 0; + this->db_cnt = 0; + } + }; + + struct ValuesForSleepState { + u8 force_set; + u8 out; + + void Reset() { + this->force_set = 0; + this->out = 0; + } + }; + private: + ddsf::IDriver &driver; + uintptr_t gpio_virtual_address; + RegisterValues register_values[GpioPadPort_Count]; + ValuesForSleepState values_for_sleep_state[GpioPadPort_Count]; + private: + uintptr_t GetGpioVirtualAddress() const { + AMS_ASSERT(this->gpio_virtual_address != 0); + return this->gpio_virtual_address; + } + public: + explicit SuspendHandler(ddsf::IDriver *drv) : driver(*drv), gpio_virtual_address(0) { + for (auto &rv : this->register_values) { + rv.Reset(); + } + for (auto &v : this->values_for_sleep_state) { + v.Reset(); + } + } + + void Initialize(uintptr_t gpio_vaddr); + + void SetValueForSleepState(TegraPad *pad, GpioValue value); + Result IsWakeEventActive(bool *out, TegraPad *pad) const; + Result SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en); + void SetWakePinDebugMode(WakePinDebugMode mode); + + void Suspend(); + void SuspendLow(); + void Resume(); + void ResumeLow(); + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_tegra_pad.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_tegra_pad.hpp new file mode 100644 index 0000000..67c0939 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_tegra_pad.hpp @@ -0,0 +1,377 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + enum GpioPadPort { + GpioPadPort_A = 0, + GpioPadPort_B = 1, + GpioPadPort_C = 2, + GpioPadPort_D = 3, + GpioPadPort_E = 4, + GpioPadPort_F = 5, + GpioPadPort_G = 6, + GpioPadPort_H = 7, + GpioPadPort_I = 8, + GpioPadPort_J = 9, + GpioPadPort_K = 10, + GpioPadPort_L = 11, + GpioPadPort_M = 12, + GpioPadPort_N = 13, + GpioPadPort_O = 14, + GpioPadPort_P = 15, + GpioPadPort_Q = 16, + GpioPadPort_R = 17, + GpioPadPort_S = 18, + GpioPadPort_T = 19, + GpioPadPort_U = 20, + GpioPadPort_V = 21, + GpioPadPort_W = 22, + GpioPadPort_X = 23, + GpioPadPort_Y = 24, + GpioPadPort_Z = 25, + GpioPadPort_AA = 26, + GpioPadPort_BB = 27, + GpioPadPort_CC = 28, + GpioPadPort_DD = 29, + GpioPadPort_EE = 30, + GpioPadPort_FF = 31, + GpioPadPort_Count = 32, + }; + + using InternalGpioPadNumber = int; + + constexpr unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { + AMS_ASSERT(which < 8); + + return (static_cast(port) * 8) + which; + } + + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_None = -1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_0 = 0x00; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_1 = 0x01; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_2 = 0x02; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_3 = 0x03; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_4 = 0x04; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_5 = 0x05; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_6 = 0x06; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_7 = 0x07; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_0 = 0x08; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_1 = 0x09; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_2 = 0x0A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_3 = 0x0B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_4 = 0x0C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_5 = 0x0D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_6 = 0x0E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_7 = 0x0F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_0 = 0x10; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_1 = 0x11; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_2 = 0x12; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_3 = 0x13; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_4 = 0x14; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_5 = 0x15; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_6 = 0x16; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_7 = 0x17; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_0 = 0x18; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_1 = 0x19; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_2 = 0x1A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_3 = 0x1B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_4 = 0x1C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_5 = 0x1D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_6 = 0x1E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_7 = 0x1F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_0 = 0x20; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_1 = 0x21; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_2 = 0x22; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_3 = 0x23; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_4 = 0x24; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_5 = 0x25; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_6 = 0x26; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_7 = 0x27; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_0 = 0x28; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_1 = 0x29; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_2 = 0x2A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_3 = 0x2B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_4 = 0x2C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_5 = 0x2D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_6 = 0x2E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_7 = 0x2F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_0 = 0x30; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_1 = 0x31; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_2 = 0x32; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_3 = 0x33; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_4 = 0x34; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_5 = 0x35; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_6 = 0x36; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_7 = 0x37; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_0 = 0x38; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_1 = 0x39; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_2 = 0x3A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_3 = 0x3B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_4 = 0x3C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_5 = 0x3D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_6 = 0x3E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_7 = 0x3F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_0 = 0x40; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_1 = 0x41; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_2 = 0x42; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_3 = 0x43; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_4 = 0x44; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_5 = 0x45; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_6 = 0x46; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_7 = 0x47; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_0 = 0x48; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_1 = 0x49; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_2 = 0x4A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_3 = 0x4B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_4 = 0x4C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_5 = 0x4D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_6 = 0x4E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_7 = 0x4F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_0 = 0x50; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_1 = 0x51; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_2 = 0x52; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_3 = 0x53; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_4 = 0x54; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_5 = 0x55; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_6 = 0x56; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_7 = 0x57; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_0 = 0x58; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_1 = 0x59; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_2 = 0x5A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_3 = 0x5B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_4 = 0x5C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_5 = 0x5D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_6 = 0x5E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_7 = 0x5F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_0 = 0x60; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_1 = 0x61; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_2 = 0x62; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_3 = 0x63; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_4 = 0x64; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_5 = 0x65; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_6 = 0x66; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_7 = 0x67; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_0 = 0x68; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_1 = 0x69; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_2 = 0x6A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_3 = 0x6B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_4 = 0x6C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_5 = 0x6D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_6 = 0x6E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_7 = 0x6F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_0 = 0x70; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_1 = 0x71; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_2 = 0x72; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_3 = 0x73; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_4 = 0x74; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_5 = 0x75; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_6 = 0x76; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_7 = 0x77; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_0 = 0x78; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_1 = 0x79; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_2 = 0x7A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_3 = 0x7B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_4 = 0x7C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_5 = 0x7D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_6 = 0x7E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_7 = 0x7F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_0 = 0x80; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_1 = 0x81; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_2 = 0x82; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_3 = 0x83; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_4 = 0x84; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_5 = 0x85; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_6 = 0x86; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_7 = 0x87; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_0 = 0x88; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_1 = 0x89; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_2 = 0x8A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_3 = 0x8B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_4 = 0x8C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_5 = 0x8D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_6 = 0x8E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_7 = 0x8F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_0 = 0x90; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_1 = 0x91; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_2 = 0x92; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_3 = 0x93; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_4 = 0x94; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_5 = 0x95; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_6 = 0x96; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_7 = 0x97; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_0 = 0x98; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_1 = 0x99; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_2 = 0x9A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_3 = 0x9B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_4 = 0x9C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_5 = 0x9D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_6 = 0x9E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_7 = 0x9F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_0 = 0xA0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_1 = 0xA1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_2 = 0xA2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_3 = 0xA3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_4 = 0xA4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_5 = 0xA5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_6 = 0xA6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_7 = 0xA7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_0 = 0xA8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_1 = 0xA9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_2 = 0xAA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_3 = 0xAB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_4 = 0xAC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_5 = 0xAD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_6 = 0xAE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_7 = 0xAF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_0 = 0xB0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_1 = 0xB1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_2 = 0xB2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_3 = 0xB3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_4 = 0xB4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_5 = 0xB5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_6 = 0xB6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_7 = 0xB7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_0 = 0xB8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_1 = 0xB9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_2 = 0xBA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_3 = 0xBB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_4 = 0xBC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_5 = 0xBD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_6 = 0xBE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_7 = 0xBF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_0 = 0xC0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_1 = 0xC1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_2 = 0xC2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_3 = 0xC3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_4 = 0xC4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_5 = 0xC5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_6 = 0xC6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_7 = 0xC7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_0 = 0xC8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_1 = 0xC9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_2 = 0xCA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_3 = 0xCB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_4 = 0xCC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_5 = 0xCD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_6 = 0xCE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_7 = 0xCF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_0 = 0xD0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_1 = 0xD1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_2 = 0xD2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_3 = 0xD3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_4 = 0xD4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_5 = 0xD5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_6 = 0xD6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_7 = 0xD7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_0 = 0xD8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_1 = 0xD9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_2 = 0xDA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_3 = 0xDB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_4 = 0xDC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_5 = 0xDD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_6 = 0xDE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_7 = 0xDF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_0 = 0xE0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_1 = 0xE1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_2 = 0xE2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_3 = 0xE3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_4 = 0xE4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_5 = 0xE5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_6 = 0xE6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_7 = 0xE7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_0 = 0xE8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_1 = 0xE9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_2 = 0xEA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_3 = 0xEB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_4 = 0xEC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_5 = 0xED; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_6 = 0xEE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_7 = 0xEF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_0 = 0xF0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_1 = 0xF1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_2 = 0xF2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_3 = 0xF3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_4 = 0xF4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_5 = 0xF5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_6 = 0xF6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_7 = 0xF7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_0 = 0xF8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_1 = 0xF9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_2 = 0xFA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_3 = 0xFB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_4 = 0xFC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_5 = 0xFD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_6 = 0xFE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_7 = 0xFF; + + struct PadMapCombination { + DeviceCode device_code; + InternalGpioPadNumber internal_number; + wec::WakeEvent wake_event; + }; + + #include "gpio_internal_pad_map_combination.inc" + + struct PadInfo { + wec::WakeEvent wake_event; + + constexpr PadInfo() : wake_event(wec::WakeEvent_None) { /* ... */ } + + constexpr explicit PadInfo(wec::WakeEvent we) : wake_event(we) { /* ... */ } + + constexpr bool operator ==(const PadInfo &rhs) const { return this->wake_event == rhs.wake_event; } + constexpr bool operator !=(const PadInfo &rhs) const { return !(*this == rhs); } + }; + + struct PadStatus { + bool is_wake_active; + bool is_wake_active_debug; + + constexpr PadStatus() : is_wake_active(false), is_wake_active_debug(false) { /* ... */ } + }; + + class TegraPad : public ::ams::gpio::driver::Pad { + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::TegraPad, ::ams::gpio::driver::Pad); + private: + using Base = ::ams::gpio::driver::Pad; + private: + util::IntrusiveListNode interrupt_list_node; + PadInfo info; + PadStatus status; + public: + using InterruptListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&TegraPad::interrupt_list_node>; + using InterruptList = typename InterruptListTraits::ListType; + friend class util::IntrusiveList>; + public: + TegraPad() : Pad(), interrupt_list_node(), info(), status() { /* ... */ } + + const PadInfo &GetInfo() const { return this->info; } + PadStatus &GetStatus() { return this->status; } + + void SetParameters(int pad, const PadInfo &i) { + Base::SetPadNumber(pad); + this->info = info; + } + + bool IsLinkedToInterruptBoundPadList() const { + return this->interrupt_list_node.IsLinked(); + } + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_wake_pin_config.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_wake_pin_config.hpp new file mode 100644 index 0000000..9b25143 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/impl/gpio_wake_pin_config.hpp @@ -0,0 +1,27 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + struct WakePinConfig { + wec::WakeEvent wake_event; + bool enable; + wec::WakeEventLevel level; + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp deleted file mode 100644 index 513add6..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 "impl/gpio_driver_impl.hpp" -#include "impl/gpio_initial_config.hpp" -#include "impl/gpio_tegra_pad.hpp" - -namespace ams::gpio::driver::board::nintendo_nx { - - namespace { - - ams::gpio::driver::board::nintendo_nx::impl::DriverImpl *g_driver_impl = nullptr; - - } - - void Initialize(bool enable_interrupt_handlers) { - /* Check that we haven't previously initialized. */ - AMS_ABORT_UNLESS(g_driver_impl == nullptr); - - /* Get the device driver subsystem framework memory resource. */ - auto *memory_resource = ddsf::GetMemoryResource(); - - /* Allocate a new driver. */ - auto *driver_storage = static_cast(memory_resource->Allocate(sizeof(*g_driver_impl))); - AMS_ABORT_UNLESS(driver_storage != nullptr); - - /* Construct the new driver. */ - g_driver_impl = new (driver_storage) ams::gpio::driver::board::nintendo_nx::impl::DriverImpl(impl::GpioRegistersPhysicalAddress, impl::GpioRegistersSize); - - /* Register the driver. */ - gpio::driver::RegisterDriver(g_driver_impl); - - /* Register interrupt handlers, if we should. */ - if (enable_interrupt_handlers) { - for (size_t i = 0; i < util::size(impl::InterruptNameTable); ++i) { - /* Allocate a handler. */ - impl::InterruptEventHandler *handler_storage = static_cast(memory_resource->Allocate(sizeof(impl::InterruptEventHandler))); - AMS_ABORT_UNLESS(handler_storage != nullptr); - - /* Initialize the handler. */ - impl::InterruptEventHandler *handler = new (handler_storage) impl::InterruptEventHandler; - handler->Initialize(g_driver_impl, impl::InterruptNameTable[i], static_cast(i)); - - /* Register the handler. */ - gpio::driver::RegisterInterruptHandler(handler); - } - } - - /* Create and register all pads. */ - for (const auto &entry : impl::PadMapCombinationList) { - /* Allocate a pad for our device. */ - impl::TegraPad *pad_storage = static_cast(memory_resource->Allocate(sizeof(impl::TegraPad))); - AMS_ABORT_UNLESS(pad_storage != nullptr); - - /* Create a pad for our device. */ - impl::TegraPad *pad = new (pad_storage) impl::TegraPad; - pad->SetParameters(entry.internal_number, impl::PadInfo{entry.wake_event}); - - /* Register the pad with our driver. */ - g_driver_impl->RegisterDevice(pad); - - /* Register the device code with our driver. */ - R_ABORT_UNLESS(gpio::driver::RegisterDeviceCode(entry.device_code, pad)); - } - } - - void SetInitialGpioConfig() { - return impl::SetInitialGpioConfig(); - } - - void SetInitialWakePinConfig() { - return impl::SetInitialWakePinConfig(); - } - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp deleted file mode 100644 index aa152fb..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* - * 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 "gpio_driver_impl.hpp" -#include "gpio_register_accessor.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - void InterruptEventHandler::Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr) { - /* Set fields. */ - this->driver = drv; - this->interrupt_name = intr; - this->controller_number = ctlr; - - /* Initialize interrupt event. */ - os::InitializeInterruptEvent(std::addressof(this->interrupt_event), intr, os::EventClearMode_ManualClear); - - /* Initialize base. */ - IEventHandler::Initialize(std::addressof(this->interrupt_event)); - } - - void InterruptEventHandler::HandleEvent() { - /* Lock the driver's interrupt mutex. */ - std::scoped_lock lk(this->driver->interrupt_control_mutex); - - /* Check each pad. */ - bool found = false; - for (auto it = this->driver->interrupt_pad_list.begin(); !found && it != this->driver->interrupt_pad_list.end(); ++it) { - found = this->CheckAndHandleInterrupt(*it); - } - - /* If we didn't find a pad, clear the interrupt event. */ - if (!found) { - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - } - } - - bool InterruptEventHandler::CheckAndHandleInterrupt(TegraPad &pad) { - /* Get the pad's number. */ - const InternalGpioPadNumber pad_number = static_cast(pad.GetPadNumber()); - - /* Check if the pad matches our controller number. */ - if (this->controller_number != ConvertInternalGpioPadNumberToController(pad_number)) { - return false; - } - - /* Get the addresses of INT_STA, INT_ENB. */ - const uintptr_t sta_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); - const uintptr_t enb_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - - /* Check if both STA and ENB are set. */ - if (reg::Read(sta_address, 1u << pad_index) == 0 || reg::Read(enb_address, 1u << pad_index) == 0) { - return false; - } - - /* The pad is signaled. First, clear the enb bit. */ - SetMaskedBit(enb_address, pad_index, 0); - reg::Read(enb_address); - - /* Disable the interrupt on the pad. */ - pad.SetInterruptEnabled(false); - this->driver->RemoveInterruptPad(std::addressof(pad)); - - /* Clear the interrupt event. */ - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - - /* Signal the pad's bound event. */ - pad.SignalInterruptBoundEvent(); - - return true; - } - - DriverImpl::DriverImpl(dd::PhysicalAddress reg_paddr, size_t size) : gpio_physical_address(reg_paddr), gpio_virtual_address(), suspend_handler(this), interrupt_pad_list(), interrupt_control_mutex() { - /* Get the corresponding virtual address for our physical address. */ - this->gpio_virtual_address = dd::QueryIoMapping(reg_paddr, size); - AMS_ABORT_UNLESS(this->gpio_virtual_address != 0); - } - - void DriverImpl::InitializeDriver() { - /* Initialize our suspend handler. */ - this->suspend_handler.Initialize(this->gpio_virtual_address); - } - - void DriverImpl::FinalizeDriver() { - /* ... */ - } - - Result DriverImpl::InitializePad(Pad *pad) { - /* Validate arguments. */ - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Configure the pad as GPIO by modifying the appropriate bit in CNF. */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_CNF, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, 1); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - - return ResultSuccess(); - } - - void DriverImpl::FinalizePad(Pad *pad) { - /* Validate arguments. */ - AMS_ASSERT(pad != nullptr); - - /* Nothing to do. */ - AMS_UNUSED(pad); - } - - Result DriverImpl::GetDirection(Direction *out, Pad *pad) const { - /* Validate arguments. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Get the pad direction by reading the appropriate bit in OE */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - - if (reg::Read(pad_address, 1u << pad_index) != 0) { - *out = Direction_Output; - } else { - *out = Direction_Input; - } - - return ResultSuccess(); - - } - - Result DriverImpl::SetDirection(Pad *pad, Direction direction) { - /* Validate arguments. */ - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Configure the pad direction by modifying the appropriate bit in OE */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, direction); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - - return ResultSuccess(); - } - - Result DriverImpl::GetValue(GpioValue *out, Pad *pad) const { - /* Validate arguments. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Get the pad value by reading the appropriate bit in IN */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - - if (reg::Read(pad_address, 1u << pad_index) != 0) { - *out = GpioValue_High; - } else { - *out = GpioValue_Low; - } - - return ResultSuccess(); - } - - Result DriverImpl::SetValue(Pad *pad, GpioValue value) { - /* Validate arguments. */ - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Configure the pad value by modifying the appropriate bit in IN */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, value); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - - return ResultSuccess(); - } - - Result DriverImpl::GetInterruptMode(InterruptMode *out, Pad *pad) const { - /* Validate arguments. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Get the pad mode by reading the appropriate bits in INT_LVL */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - - switch ((reg::Read(pad_address) >> pad_index) & InternalInterruptMode_Mask) { - case InternalInterruptMode_LowLevel: *out = InterruptMode_LowLevel; break; - case InternalInterruptMode_HighLevel: *out = InterruptMode_HighLevel; break; - case InternalInterruptMode_RisingEdge: *out = InterruptMode_RisingEdge; break; - case InternalInterruptMode_FallingEdge: *out = InterruptMode_FallingEdge; break; - case InternalInterruptMode_AnyEdge: *out = InterruptMode_AnyEdge; break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - return ResultSuccess(); - } - - Result DriverImpl::SetInterruptMode(Pad *pad, InterruptMode mode) { - /* Validate arguments. */ - AMS_ASSERT(pad != nullptr); - - /* Convert the pad to an internal number. */ - const InternalGpioPadNumber pad_number = pad->GetPadNumber(); - - /* Configure the pad mode by modifying the appropriate bits in INT_LVL */ - const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - - switch (mode) { - case InterruptMode_LowLevel: reg::ReadWrite(pad_address, InternalInterruptMode_LowLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; - case InterruptMode_HighLevel: reg::ReadWrite(pad_address, InternalInterruptMode_HighLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; - case InterruptMode_RisingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_RisingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; - case InterruptMode_FallingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_FallingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; - case InterruptMode_AnyEdge: reg::ReadWrite(pad_address, InternalInterruptMode_AnyEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - - return ResultSuccess(); - } - - Result DriverImpl::SetInterruptEnabled(Pad *pad, bool en) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::GetInterruptStatus(InterruptStatus *out, Pad *pad) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::ClearInterruptStatus(Pad *pad) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::GetDebounceEnabled(bool *out, Pad *pad) const { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SetDebounceEnabled(Pad *pad, bool en) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::GetDebounceTime(s32 *out_ms, Pad *pad) const { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SetDebounceTime(Pad *pad, s32 ms) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::GetUnknown22(u32 *out) { - /* TODO */ - AMS_ABORT(); - } - - void DriverImpl::Unknown23() { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SetValueForSleepState(Pad *pad, GpioValue value) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::IsWakeEventActive(bool *out, Pad *pad) const { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SetWakePinDebugMode(WakePinDebugMode mode) { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::Suspend() { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::SuspendLow() { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::Resume() { - /* TODO */ - AMS_ABORT(); - } - - Result DriverImpl::ResumeLow() { - /* TODO */ - AMS_ABORT(); - } - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp deleted file mode 100644 index b78f341..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#include "gpio_tegra_pad.hpp" -#include "gpio_register_accessor.hpp" -#include "gpio_suspend_handler.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - class DriverImpl; - - class InterruptEventHandler : public ddsf::IEventHandler { - private: - DriverImpl *driver; - os::InterruptName interrupt_name; - os::InterruptEventType interrupt_event; - int controller_number; - private: - bool CheckAndHandleInterrupt(TegraPad &pad); - public: - InterruptEventHandler() : IEventHandler(), driver(nullptr), interrupt_name(), interrupt_event(), controller_number() { /* ... */ } - - void Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr); - - virtual void HandleEvent() override; - }; - - class DriverImpl : public ::ams::gpio::driver::IGpioDriver { - NON_COPYABLE(DriverImpl); - NON_MOVEABLE(DriverImpl); - AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::DriverImpl, ::ams::gpio::driver::IGpioDriver); - friend class InterruptEventHandler; - private: - dd::PhysicalAddress gpio_physical_address; - uintptr_t gpio_virtual_address; - SuspendHandler suspend_handler; - TegraPad::InterruptList interrupt_pad_list; - mutable os::SdkMutex interrupt_control_mutex; - public: - DriverImpl(dd::PhysicalAddress reg_paddr, size_t size); - - virtual void InitializeDriver() override; - virtual void FinalizeDriver() override; - - virtual Result InitializePad(Pad *pad) override; - virtual void FinalizePad(Pad *pad) override; - - virtual Result GetDirection(Direction *out, Pad *pad) const override; - virtual Result SetDirection(Pad *pad, Direction direction) override; - - virtual Result GetValue(GpioValue *out, Pad *pad) const override; - virtual Result SetValue(Pad *pad, GpioValue value) override; - - virtual Result GetInterruptMode(InterruptMode *out, Pad *pad) const override; - virtual Result SetInterruptMode(Pad *pad, InterruptMode mode) override; - - virtual Result SetInterruptEnabled(Pad *pad, bool en) override; - - virtual Result GetInterruptStatus(InterruptStatus *out, Pad *pad) override; - virtual Result ClearInterruptStatus(Pad *pad) override; - - virtual os::SdkMutex &GetInterruptControlMutex(const Pad &pad) const override { - AMS_UNUSED(pad); - return this->interrupt_control_mutex; - } - - virtual Result GetDebounceEnabled(bool *out, Pad *pad) const override; - virtual Result SetDebounceEnabled(Pad *pad, bool en) override; - - virtual Result GetDebounceTime(s32 *out_ms, Pad *pad) const override; - virtual Result SetDebounceTime(Pad *pad, s32 ms) override; - - virtual Result GetUnknown22(u32 *out) override; - virtual void Unknown23() override; - - virtual Result SetValueForSleepState(Pad *pad, GpioValue value) override; - virtual Result IsWakeEventActive(bool *out, Pad *pad) const override; - virtual Result SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) override; - virtual Result SetWakePinDebugMode(WakePinDebugMode mode) override; - - virtual Result Suspend() override; - virtual Result SuspendLow() override; - virtual Result Resume() override; - virtual Result ResumeLow() override; - private: - static constexpr ALWAYS_INLINE TegraPad &GetTegraPad(Pad *pad) { - AMS_ASSERT(pad != nullptr); - return static_cast(*pad); - } - - static ALWAYS_INLINE const PadInfo &GetInfo(Pad *pad) { - return GetTegraPad(pad).GetInfo(); - } - - static ALWAYS_INLINE PadStatus &GetStatus(Pad *pad) { - return GetTegraPad(pad).GetStatus(); - } - - void AddInterruptPad(TegraPad *pad) { - AMS_ASSERT(pad != nullptr); - if (!pad->IsLinkedToInterruptBoundPadList()) { - this->interrupt_pad_list.push_back(*pad); - } - } - - void RemoveInterruptPad(TegraPad *pad) { - AMS_ASSERT(pad != nullptr); - if (pad->IsLinkedToInterruptBoundPadList()) { - this->interrupt_pad_list.erase(this->interrupt_pad_list.iterator_to(*pad)); - } - } - }; - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp deleted file mode 100644 index b6b1022..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 "gpio_driver_impl.hpp" -#include "gpio_initial_config.hpp" -#include "gpio_wake_pin_config.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - namespace { - - spl::HardwareType GetHardwareType() { - /* Acquire access to spl: */ - sm::ScopedServiceHolder spl_holder; - AMS_ABORT_UNLESS(static_cast(spl_holder)); - - /* Get config. */ - return ::ams::spl::GetHardwareType(); - } - - #include "gpio_initial_wake_pin_config_icosa.inc" - /* #include "gpio_initial_wake_pin_config_copper.inc" */ - #include "gpio_initial_wake_pin_config_hoag.inc" - #include "gpio_initial_wake_pin_config_iowa.inc" - #include "gpio_initial_wake_pin_config_calcio.inc" - #include "gpio_initial_wake_pin_config_aula.inc" - - #include "gpio_initial_config_icosa.inc" - /* #include "gpio_initial_config_copper.inc" */ - #include "gpio_initial_config_hoag.inc" - #include "gpio_initial_config_iowa.inc" - #include "gpio_initial_config_calcio.inc" - #include "gpio_initial_config_aula.inc" - - } - - void SetInitialGpioConfig() { - /* Set wake event levels, wake event enables. */ - const GpioInitialConfig *configs = nullptr; - size_t num_configs = 0; - - /* Select the correct config. */ - switch (GetHardwareType()) { - case spl::HardwareType::Icosa: - configs = InitialGpioConfigsIcosa; - num_configs = NumInitialGpioConfigsIcosa; - break; - case spl::HardwareType::Hoag: - configs = InitialGpioConfigsHoag; - num_configs = NumInitialGpioConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = InitialGpioConfigsIowa; - num_configs = NumInitialGpioConfigsIowa; - break; - case spl::HardwareType::Calcio: - configs = InitialGpioConfigsCalcio; - num_configs = NumInitialGpioConfigsCalcio; - break; - case spl::HardwareType::Aula: - configs = InitialGpioConfigsAula; - num_configs = NumInitialGpioConfigsAula; - break; - case spl::HardwareType::Copper: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Check we can use our config. */ - AMS_ABORT_UNLESS(configs != nullptr); - - /* Apply the configs. */ - { - /* Create a driver to use for the duration of our application. */ - DriverImpl driver(GpioRegistersPhysicalAddress, GpioRegistersSize); - driver.InitializeDriver(); - - for (size_t i = 0; i < num_configs; ++i) { - /* Find the internal pad number for our device. */ - bool found = false; - for (const auto &entry : PadMapCombinationList) { - if (entry.device_code == configs[i].device_code) { - /* We found an entry. */ - found = true; - - /* Create a pad for our device. */ - TegraPad pad; - pad.SetParameters(entry.internal_number, PadInfo{entry.wake_event}); - - /* Initialize the pad. */ - R_ABORT_UNLESS(driver.InitializePad(std::addressof(pad))); - - /* Set the direction. */ - R_ABORT_UNLESS(driver.SetDirection(std::addressof(pad), configs[i].direction)); - - /* If the direction is output, set the value. */ - if (configs[i].direction == Direction_Output) { - R_ABORT_UNLESS(driver.SetValue(std::addressof(pad), configs[i].value)); - } - - /* Finalize the pad we made. */ - driver.FinalizePad(std::addressof(pad)); - break; - } - } - - /* Ensure that we applied the config for the pad we wanted. */ - AMS_ABORT_UNLESS(found); - } - - /* Finalize the driver. */ - driver.FinalizeDriver(); - } - } - - void SetInitialWakePinConfig() { - /* Ensure the wec driver is initialized. */ - ams::wec::Initialize(); - - /* Set wake event levels, wake event enables. */ - const WakePinConfig *configs = nullptr; - size_t num_configs = 0; - - /* Select the correct config. */ - switch (GetHardwareType()) { - case spl::HardwareType::Icosa: - configs = InitialWakePinConfigsIcosa; - num_configs = NumInitialWakePinConfigsIcosa; - break; - case spl::HardwareType::Hoag: - configs = InitialWakePinConfigsHoag; - num_configs = NumInitialWakePinConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = InitialWakePinConfigsIowa; - num_configs = NumInitialWakePinConfigsIowa; - break; - case spl::HardwareType::Calcio: - configs = InitialWakePinConfigsCalcio; - num_configs = NumInitialWakePinConfigsCalcio; - break; - case spl::HardwareType::Aula: - configs = InitialWakePinConfigsAula; - num_configs = NumInitialWakePinConfigsAula; - break; - case spl::HardwareType::Copper: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Check we can use our config. */ - AMS_ABORT_UNLESS(configs != nullptr); - - /* Apply the config. */ - for (size_t i = 0; i < num_configs; ++i) { - wec::SetWakeEventLevel(configs[i].wake_event, configs[i].level); - wec::SetWakeEventEnabled(configs[i].wake_event, configs[i].enable); - } - } - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.hpp deleted file mode 100644 index d577ccc..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - struct GpioInitialConfig { - DeviceCode device_code; - gpio::Direction direction; - gpio::GpioValue value; - }; - - void SetInitialGpioConfig(); - void SetInitialWakePinConfig(); - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_aula.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_aula.inc deleted file mode 100644 index e1c90ee..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_aula.inc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const GpioInitialConfig InitialGpioConfigsAula[] = { - { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, - { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, - { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, - { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, - { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, - { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, - { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, - { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, - { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, - { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, - { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, - { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, - { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, - { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, - { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, - { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, - { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, - { DeviceCode_SdCd, Direction_Input, GpioValue_High }, - { DeviceCode_SdWp, Direction_Input, GpioValue_High }, - { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, - { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, - { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, - { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, - { DeviceCode_GpioPortH0, Direction_Input, GpioValue_Low }, - { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, - { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, - { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, - { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, -}; - -constexpr inline size_t NumInitialGpioConfigsAula = util::size(InitialGpioConfigsAula); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc deleted file mode 100644 index e20acc0..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const GpioInitialConfig InitialGpioConfigsCalcio[] = { - { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, - { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, - { DeviceCode_GpioPortF1, Direction_Output, GpioValue_Low }, - { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, - { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, - { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, - { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, - { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, - { DeviceCode_SdCd, Direction_Input, GpioValue_High }, - { DeviceCode_SdWp, Direction_Input, GpioValue_High }, - { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, - { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, - { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_Hdmi5VEn, Direction_Output, GpioValue_Low }, - { DeviceCode_UsbSwitchB1Oc, Direction_Input, GpioValue_High }, - { DeviceCode_HdmiPdTrEn, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, - { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, - { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, - { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, - { DeviceCode_UsbSwitchB1En, Direction_Output, GpioValue_Low }, - { DeviceCode_HdmiHpd, Direction_Input, GpioValue_Low }, -}; - -constexpr inline size_t NumInitialGpioConfigsCalcio = util::size(InitialGpioConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc deleted file mode 100644 index 97a45b2..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const GpioInitialConfig InitialGpioConfigsHoag[] = { - { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, - { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, - { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, - { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, - { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, - { DeviceCode_McuIrq, Direction_Input, GpioValue_High }, - { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, - { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, - { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, - { DeviceCode_McuBoot, Direction_Output, GpioValue_Low }, - { DeviceCode_McuRst, Direction_Output, GpioValue_Low }, - { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, - { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, - { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, - { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, - { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, - { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, - { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, - { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, - { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, - { DeviceCode_LcdGpio1, Direction_Input, GpioValue_High }, - { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, - { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, - { DeviceCode_SdCd, Direction_Input, GpioValue_High }, - { DeviceCode_SdWp, Direction_Input, GpioValue_High }, - { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, - { DeviceCode_NfcEn, Direction_Output, GpioValue_Low }, - { DeviceCode_NfcIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, - { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, - { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, - { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_NfcRst, Direction_Output, GpioValue_Low }, - { DeviceCode_GpioPortC7, Direction_Input, GpioValue_Low }, - { DeviceCode_GpioPortD0, Direction_Input, GpioValue_Low }, - { DeviceCode_GpioPortC5, Direction_Input, GpioValue_Low }, - { DeviceCode_GpioPortC6, Direction_Input, GpioValue_Low }, - { DeviceCode_GpioPortY7, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, - { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, - { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, - { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, - { DeviceCode_McuPor, Direction_Output, GpioValue_Low }, -}; - -constexpr inline size_t NumInitialGpioConfigsHoag = util::size(InitialGpioConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc deleted file mode 100644 index 85800f5..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const GpioInitialConfig InitialGpioConfigsIcosa[] = { - { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, - { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, - { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, - { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowGc, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, - { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, - { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, - { DeviceCode_FanTach, Direction_Input, GpioValue_High }, - { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, - { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, - { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, - { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, - { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, - { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_High }, - { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, - { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, - { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, - { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, - { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_High }, - { DeviceCode_SdCd, Direction_Input, GpioValue_High }, - { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, - { DeviceCode_SdWp, Direction_Input, GpioValue_High }, - { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, - { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, - { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, - { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, - { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_Max77621GpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, - { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, - { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, - { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, - { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, - { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, - { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, - { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, -}; - -constexpr inline size_t NumInitialGpioConfigsIcosa = util::size(InitialGpioConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc deleted file mode 100644 index 1b71bd7..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const GpioInitialConfig InitialGpioConfigsIowa[] = { - { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, - { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, - { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, - { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, - { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, - { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, - { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, - { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, - { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, - { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, - { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, - { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, - { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, - { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, - { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, - { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, - { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, - { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, - { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, - { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, - { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, - { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, - { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, - { DeviceCode_SdCd, Direction_Input, GpioValue_High }, - { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, - { DeviceCode_SdWp, Direction_Input, GpioValue_High }, - { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, - { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, - { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, - { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, - { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, - { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, - { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, - { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, - { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, - { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, - { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, - { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, - { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, - { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, - { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, - { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, - { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, - { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, -}; - -constexpr inline size_t NumInitialGpioConfigsIowa = util::size(InitialGpioConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_aula.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_aula.inc deleted file mode 100644 index d955382..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_aula.inc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ - -constexpr inline const WakePinConfig InitialWakePinConfigsAula[] = { - { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, - /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ - { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_RtcIrq, }, */ - { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_Utmip0, }, */ - /* { ams::wec::WakeEvent_Utmip1, }, */ - /* { ams::wec::WakeEvent_Utmip2, }, */ - /* { ams::wec::WakeEvent_Utmip3, }, */ - /* { ams::wec::WakeEvent_Uhsic, }, */ - /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ - { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, -}; - -constexpr inline size_t NumInitialWakePinConfigsAula = util::size(InitialWakePinConfigsAula); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc deleted file mode 100644 index 6cdd662..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ - -constexpr inline const WakePinConfig InitialWakePinConfigsCalcio[] = { - { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ExtconDetS, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart3Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, - /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ - { ams::wec::WakeEvent_ExtconDetU, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_RtcIrq, }, */ - { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonSlideSw, false, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_AlsProxInt, }, */ - { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Bq24190Irq, false, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_Utmip0, }, */ - /* { ams::wec::WakeEvent_Utmip1, }, */ - /* { ams::wec::WakeEvent_Utmip2, }, */ - /* { ams::wec::WakeEvent_Utmip3, }, */ - /* { ams::wec::WakeEvent_Uhsic, }, */ - /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ - { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, -}; - -constexpr inline size_t NumInitialWakePinConfigsCalcio = util::size(InitialWakePinConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc deleted file mode 100644 index 177fcc5..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ - -constexpr inline const WakePinConfig InitialWakePinConfigsHoag[] = { - { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_McuIrq, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, - /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ - { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_RtcIrq, }, */ - { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_Utmip0, }, */ - /* { ams::wec::WakeEvent_Utmip1, }, */ - /* { ams::wec::WakeEvent_Utmip2, }, */ - /* { ams::wec::WakeEvent_Utmip3, }, */ - /* { ams::wec::WakeEvent_Uhsic, }, */ - /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ - { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio1, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, -}; - -constexpr inline size_t NumInitialWakePinConfigsHoag = util::size(InitialWakePinConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc deleted file mode 100644 index 19c40e6..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ - -constexpr inline const WakePinConfig InitialWakePinConfigsIcosa[] = { - { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, - /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ - { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_RtcIrq, }, */ - { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_AlsProxInt, }, */ - { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_Utmip0, }, */ - /* { ams::wec::WakeEvent_Utmip1, }, */ - /* { ams::wec::WakeEvent_Utmip2, }, */ - /* { ams::wec::WakeEvent_Utmip3, }, */ - /* { ams::wec::WakeEvent_Uhsic, }, */ - /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ - { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, -}; - -constexpr inline size_t NumInitialWakePinConfigsIcosa = util::size(InitialWakePinConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc deleted file mode 100644 index ebd8016..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ - -constexpr inline const WakePinConfig InitialWakePinConfigsIowa[] = { - { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, - /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ - { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_RtcIrq, }, */ - { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_AlsProxInt, }, */ - { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, - /* { ams::wec::WakeEvent_Utmip0, }, */ - /* { ams::wec::WakeEvent_Utmip1, }, */ - /* { ams::wec::WakeEvent_Utmip2, }, */ - /* { ams::wec::WakeEvent_Utmip3, }, */ - /* { ams::wec::WakeEvent_Uhsic, }, */ - /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ - { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, - { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, - { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, -}; - -constexpr inline size_t NumInitialWakePinConfigsIowa = util::size(InitialWakePinConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc deleted file mode 100644 index a9ff372..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ - -constexpr inline const PadMapCombination PadMapCombinationList[] = { - { DeviceCode_CodecLdoEnTemp, InternalGpioPadNumber_Port_Z_4, ams::wec::WakeEvent_None }, - { DeviceCode_PowSdEn, InternalGpioPadNumber_Port_E_4, ams::wec::WakeEvent_None }, - { DeviceCode_BtRst, InternalGpioPadNumber_Port_H_4, ams::wec::WakeEvent_None }, - { DeviceCode_RamCode3, InternalGpioPadNumber_Port_BB_2, ams::wec::WakeEvent_None }, - { DeviceCode_GameCardReset, InternalGpioPadNumber_Port_BB_3, ams::wec::WakeEvent_None }, - { DeviceCode_CodecAlert, InternalGpioPadNumber_Port_BB_4, ams::wec::WakeEvent_None }, - { DeviceCode_PowGc, InternalGpioPadNumber_Port_E_5, ams::wec::WakeEvent_None }, - { DeviceCode_DebugControllerDet, InternalGpioPadNumber_Port_S_0, ams::wec::WakeEvent_None }, - { DeviceCode_BattChgStatus, InternalGpioPadNumber_Port_S_1, ams::wec::WakeEvent_None }, - { DeviceCode_BattChgEnableN, InternalGpioPadNumber_Port_S_6, ams::wec::WakeEvent_None }, - { DeviceCode_FanTach, InternalGpioPadNumber_Port_S_7, ams::wec::WakeEvent_None }, - { DeviceCode_Vdd50AEn, InternalGpioPadNumber_Port_A_5, ams::wec::WakeEvent_None }, - { DeviceCode_SdevCoaxSel1, InternalGpioPadNumber_Port_P_0, ams::wec::WakeEvent_None }, - { DeviceCode_ProdType0, InternalGpioPadNumber_Port_P_5, ams::wec::WakeEvent_None }, - { DeviceCode_ProdType1, InternalGpioPadNumber_Port_P_4, ams::wec::WakeEvent_None }, - { DeviceCode_ProdType2, InternalGpioPadNumber_Port_P_3, ams::wec::WakeEvent_None }, - { DeviceCode_ProdType3, InternalGpioPadNumber_Port_P_2, ams::wec::WakeEvent_None }, - { DeviceCode_TempAlert, InternalGpioPadNumber_Port_X_4, ams::wec::WakeEvent_None }, - { DeviceCode_CodecHpDetIrq, InternalGpioPadNumber_Port_V_6, ams::wec::WakeEvent_None }, - { DeviceCode_MotionInt, InternalGpioPadNumber_Port_X_2, ams::wec::WakeEvent_None }, - { DeviceCode_TpIrq, InternalGpioPadNumber_Port_X_1, ams::wec::WakeEvent_None }, - { DeviceCode_ButtonSleep2, InternalGpioPadNumber_Port_X_5, ams::wec::WakeEvent_None }, - { DeviceCode_ButtonVolUp, InternalGpioPadNumber_Port_X_6, ams::wec::WakeEvent_None }, - { DeviceCode_ButtonVolDn, InternalGpioPadNumber_Port_X_7, ams::wec::WakeEvent_None }, - { DeviceCode_RecoveryKey, InternalGpioPadNumber_Port_Y_1, ams::wec::WakeEvent_None }, - { DeviceCode_PowLcdBlEn, InternalGpioPadNumber_Port_V_1, ams::wec::WakeEvent_None }, - { DeviceCode_LcdReset, InternalGpioPadNumber_Port_V_2, ams::wec::WakeEvent_None }, - { DeviceCode_PdVconnEn, InternalGpioPadNumber_Port_K_5, ams::wec::WakeEvent_None }, - { DeviceCode_PdRstN, InternalGpioPadNumber_Port_V_5, ams::wec::WakeEvent_None }, - { DeviceCode_SdevCoaxSel0, InternalGpioPadNumber_Port_Z_2, ams::wec::WakeEvent_None }, - { DeviceCode_SdWp, InternalGpioPadNumber_Port_Z_3, ams::wec::WakeEvent_None }, - { DeviceCode_TpReset, InternalGpioPadNumber_Port_J_7, ams::wec::WakeEvent_None }, - { DeviceCode_BtGpio2, InternalGpioPadNumber_Port_K_0, ams::wec::WakeEvent_None }, - { DeviceCode_BtGpio3, InternalGpioPadNumber_Port_K_1, ams::wec::WakeEvent_None }, - { DeviceCode_BtGpio4, InternalGpioPadNumber_Port_K_2, ams::wec::WakeEvent_None }, - { DeviceCode_PowVcpuInt, InternalGpioPadNumber_Port_K_6, ams::wec::WakeEvent_None }, - { DeviceCode_Max77621GpuInt, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, - { DeviceCode_ExtconChgU, InternalGpioPadNumber_Port_K_3, ams::wec::WakeEvent_None }, - { DeviceCode_ExtconChgS, InternalGpioPadNumber_Port_CC_3, ams::wec::WakeEvent_None }, - { DeviceCode_WifiRfDisable, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, - { DeviceCode_WifiReset, InternalGpioPadNumber_Port_H_1, ams::wec::WakeEvent_None }, - { DeviceCode_ApWakeBt, InternalGpioPadNumber_Port_H_3, ams::wec::WakeEvent_None }, - { DeviceCode_BtGpio5, InternalGpioPadNumber_Port_H_7, ams::wec::WakeEvent_None }, - { DeviceCode_PowLcdVddPEn, InternalGpioPadNumber_Port_I_0, ams::wec::WakeEvent_None }, - { DeviceCode_PowLcdVddNEn, InternalGpioPadNumber_Port_I_1, ams::wec::WakeEvent_None }, - { DeviceCode_RamCode2, InternalGpioPadNumber_Port_CC_2, ams::wec::WakeEvent_None }, - { DeviceCode_Vdd50BEn, InternalGpioPadNumber_Port_CC_4, ams::wec::WakeEvent_None }, - { DeviceCode_OtgFet1ForSdev, InternalGpioPadNumber_Port_J_5, ams::wec::WakeEvent_None }, - { DeviceCode_OtgFet2ForSdev, InternalGpioPadNumber_Port_L_0, ams::wec::WakeEvent_None }, - { DeviceCode_ExtConWakeU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_None }, - { DeviceCode_ExtConWakeS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_None }, - { DeviceCode_ExtUart2Rts, InternalGpioPadNumber_Port_G_2, ams::wec::WakeEvent_None }, - { DeviceCode_ExtUart3Rts, InternalGpioPadNumber_Port_D_3, ams::wec::WakeEvent_None }, - { DeviceCode_Debug0, InternalGpioPadNumber_Port_E_0, ams::wec::WakeEvent_None }, - { DeviceCode_Debug1, InternalGpioPadNumber_Port_E_1, ams::wec::WakeEvent_None }, - { DeviceCode_Debug2, InternalGpioPadNumber_Port_E_2, ams::wec::WakeEvent_None }, - { DeviceCode_Debug3, InternalGpioPadNumber_Port_E_3, ams::wec::WakeEvent_None }, - { DeviceCode_NfcIrq, InternalGpioPadNumber_Port_J_4, ams::wec::WakeEvent_None }, - { DeviceCode_NfcRst, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, - { DeviceCode_McuIrq, InternalGpioPadNumber_Port_E_7, ams::wec::WakeEvent_McuIrq }, - { DeviceCode_McuBoot, InternalGpioPadNumber_Port_T_0, ams::wec::WakeEvent_None }, - { DeviceCode_McuRst, InternalGpioPadNumber_Port_T_1, ams::wec::WakeEvent_None }, - { DeviceCode_Vdd5V3En, InternalGpioPadNumber_Port_X_3, ams::wec::WakeEvent_None }, - { DeviceCode_McuPor, InternalGpioPadNumber_Port_CC_5, ams::wec::WakeEvent_None }, - { DeviceCode_NfcEn, InternalGpioPadNumber_Port_J_6, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortC7, InternalGpioPadNumber_Port_C_7, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortD0, InternalGpioPadNumber_Port_D_0, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortC5, InternalGpioPadNumber_Port_C_5, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortC6, InternalGpioPadNumber_Port_C_6, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortY7, InternalGpioPadNumber_Port_Y_5, ams::wec::WakeEvent_None }, - { DeviceCode_Hdmi5VEn, InternalGpioPadNumber_Port_C_0, ams::wec::WakeEvent_None }, - { DeviceCode_UsbSwitchB1En, InternalGpioPadNumber_Port_C_1, ams::wec::WakeEvent_None }, - { DeviceCode_HdmiPdTrEn, InternalGpioPadNumber_Port_C_2, ams::wec::WakeEvent_None }, - { DeviceCode_UsbSwitchB1Oc, InternalGpioPadNumber_Port_CC_6, ams::wec::WakeEvent_None }, - { DeviceCode_HdmiHpd, InternalGpioPadNumber_Port_CC_1, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortF1, InternalGpioPadNumber_Port_F_1, ams::wec::WakeEvent_None }, - { DeviceCode_GpioPortH0, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, - { DeviceCode_ExtconDetS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_ExtconDetS }, - { DeviceCode_GameCardCd, InternalGpioPadNumber_Port_S_3, ams::wec::WakeEvent_CamI2cSda }, - { DeviceCode_BattMgicIrq, InternalGpioPadNumber_Port_Y_0, ams::wec::WakeEvent_ButtonSlideSw }, - { DeviceCode_Bq24190Irq, InternalGpioPadNumber_Port_Z_0, ams::wec::WakeEvent_Bq24190Irq }, - { DeviceCode_CradleIrq, InternalGpioPadNumber_Port_K_4, ams::wec::WakeEvent_CradleIrq }, - { DeviceCode_BtWakeAp, InternalGpioPadNumber_Port_H_5, ams::wec::WakeEvent_BtWakeAp }, - { DeviceCode_ExtconDetU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_ExtconDetU }, - { DeviceCode_WifiWakeHost, InternalGpioPadNumber_Port_H_2, ams::wec::WakeEvent_WifiWakeAp }, - { DeviceCode_SdCd, InternalGpioPadNumber_Port_Z_1, ams::wec::WakeEvent_SdCd }, - { DeviceCode_ExtUart2Cts, InternalGpioPadNumber_Port_G_3, ams::wec::WakeEvent_Uart2Cts }, - { DeviceCode_ExtUart3Cts, InternalGpioPadNumber_Port_D_4, ams::wec::WakeEvent_Uart3Cts }, - { DeviceCode_LcdGpio1, InternalGpioPadNumber_Port_V_3, ams::wec::WakeEvent_LcdGpio1 }, - { DeviceCode_PmuIrq, InternalGpioPadNumber_None, ams::wec::WakeEvent_PwrIntN }, -}; - -constexpr inline size_t PadMapCombinationListSize = util::size(PadMapCombinationList); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp deleted file mode 100644 index c9b2f6e..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#include "gpio_tegra_pad.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000D000; - constexpr inline size_t GpioRegistersSize = 4_KB; - - constexpr inline int GpioPerControllerBitWidth = 5; - constexpr inline int GpioControllerBitWidth = 3; - - constexpr inline int PortPerControllerBitWidth = 2; - constexpr inline int PortPerController = (1 << PortPerControllerBitWidth); - - constexpr inline int GpioPerPortBitWidth = 3; - constexpr inline int GpioPerPort = (1 << GpioPerPortBitWidth); - - static_assert(PortPerControllerBitWidth + GpioPerPortBitWidth == GpioPerControllerBitWidth); - static_assert(PortPerController * GpioPerPort == (1 << GpioPerControllerBitWidth)); - - constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { - return (number >> GpioPerControllerBitWidth); - } - - constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { - return (number >> GpioControllerBitWidth); - } - - constexpr InternalGpioPadNumber ConvertPortToInternalGpioPadNumber(int port) { - return static_cast(port << GpioControllerBitWidth); - } - - constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { - return (number & (GpioPerPort - 1)); - } - - constexpr int ConvertPortNumberToOffset(int port_number) { - return (port_number & (PortPerController - 1)); - } - - enum GpioController { - GpioController_1 = 0, - GpioController_2 = 1, - GpioController_3 = 2, - GpioController_4 = 3, - GpioController_5 = 4, - GpioController_6 = 5, - GpioController_7 = 6, - GpioController_8 = 7, - GpioController_Count = 8, - }; - static_assert(GpioController_Count == static_cast(1 << GpioControllerBitWidth)); - - constexpr inline os::InterruptName InterruptNameTable[GpioController_Count] = { - 64, /* GpioController_1 */ - 65, /* GpioController_2 */ - 66, /* GpioController_3 */ - 67, /* GpioController_4 */ - 87, /* GpioController_5 */ - 119, /* GpioController_6 */ - 121, /* GpioController_7 */ - 157, /* GpioController_8 */ - }; - - enum InternalInterruptMode { - InternalInterruptMode_LowLevel = 0x000000, - InternalInterruptMode_HighLevel = 0x000001, - InternalInterruptMode_RisingEdge = 0x000101, - InternalInterruptMode_FallingEdge = 0x000100, - InternalInterruptMode_AnyEdge = 0x010100, - - InternalInterruptMode_Mask = 0x010101, - }; - - enum GpioRegisterType { - GpioRegisterType_GPIO_CNF = 0, - GpioRegisterType_GPIO_OE = 1, - GpioRegisterType_GPIO_OUT = 2, - GpioRegisterType_GPIO_IN = 3, - GpioRegisterType_GPIO_INT_STA = 4, - GpioRegisterType_GPIO_INT_ENB = 5, - GpioRegisterType_GPIO_INT_LVL = 6, - GpioRegisterType_GPIO_INT_CLR = 7, - GpioRegisterType_GPIO_DB_CTRL = 8, - GpioRegisterType_GPIO_DB_CNT = 9, - }; - - constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; - constexpr inline int MaskedWriteBitOffset = 8; - - constexpr inline uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { - const auto controller = ConvertInternalGpioPadNumberToController(pad_number); - const auto port = ConvertInternalGpioPadNumberToPort(pad_number); - const auto offset = ConvertPortNumberToOffset(port); - - switch (reg_type) { - default: - return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); - case GpioRegisterType_GPIO_DB_CTRL: - return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); - case GpioRegisterType_GPIO_DB_CNT: - return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); - } - } - - inline void SetMaskedBit(uintptr_t pad_address, int index, int value) { - const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; - - reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); - } - - inline void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { - const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; - - reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); - } - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp deleted file mode 100644 index a922b21..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 "gpio_suspend_handler.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - void SuspendHandler::Initialize(uintptr_t gpio_vaddr) { - /* Set our gpio virtual address. */ - this->gpio_virtual_address = gpio_vaddr; - - /* Ensure that we can use the wec library. */ - ams::wec::Initialize(); - } - - void SuspendHandler::SetValueForSleepState(TegraPad *pad, GpioValue value) { - /* TODO */ - AMS_ABORT(); - } - - Result SuspendHandler::IsWakeEventActive(bool *out, TegraPad *pad) const { - /* TODO */ - AMS_ABORT(); - } - - Result SuspendHandler::SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en) { - /* TODO */ - AMS_ABORT(); - } - - void SuspendHandler::SetWakePinDebugMode(WakePinDebugMode mode) { - /* TODO */ - AMS_ABORT(); - } - - void SuspendHandler::Suspend() { - /* TODO */ - AMS_ABORT(); - } - - void SuspendHandler::SuspendLow() { - /* TODO */ - AMS_ABORT(); - } - - void SuspendHandler::Resume() { - /* TODO */ - AMS_ABORT(); - } - - void SuspendHandler::ResumeLow() { - /* TODO */ - AMS_ABORT(); - } - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp deleted file mode 100644 index faaf59b..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#include "gpio_tegra_pad.hpp" - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - class SuspendHandler { - NON_COPYABLE(SuspendHandler); - NON_MOVEABLE(SuspendHandler); - private: - struct RegisterValues { - u16 conf; - u8 oe; - u8 out; - u8 int_enb; - u32 int_lvl; - u8 db_ctrl; - u8 db_cnt; - - void Reset() { - this->conf = 0; - this->oe = 0; - this->out = 0; - this->int_enb = 0; - this->int_lvl = 0; - this->db_ctrl = 0; - this->db_cnt = 0; - } - }; - - struct ValuesForSleepState { - u8 force_set; - u8 out; - - void Reset() { - this->force_set = 0; - this->out = 0; - } - }; - private: - ddsf::IDriver &driver; - uintptr_t gpio_virtual_address; - RegisterValues register_values[GpioPadPort_Count]; - ValuesForSleepState values_for_sleep_state[GpioPadPort_Count]; - private: - uintptr_t GetGpioVirtualAddress() const { - AMS_ASSERT(this->gpio_virtual_address != 0); - return this->gpio_virtual_address; - } - public: - explicit SuspendHandler(ddsf::IDriver *drv) : driver(*drv), gpio_virtual_address(0) { - for (auto &rv : this->register_values) { - rv.Reset(); - } - for (auto &v : this->values_for_sleep_state) { - v.Reset(); - } - } - - void Initialize(uintptr_t gpio_vaddr); - - void SetValueForSleepState(TegraPad *pad, GpioValue value); - Result IsWakeEventActive(bool *out, TegraPad *pad) const; - Result SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en); - void SetWakePinDebugMode(WakePinDebugMode mode); - - void Suspend(); - void SuspendLow(); - void Resume(); - void ResumeLow(); - }; - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp deleted file mode 100644 index 67c0939..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - enum GpioPadPort { - GpioPadPort_A = 0, - GpioPadPort_B = 1, - GpioPadPort_C = 2, - GpioPadPort_D = 3, - GpioPadPort_E = 4, - GpioPadPort_F = 5, - GpioPadPort_G = 6, - GpioPadPort_H = 7, - GpioPadPort_I = 8, - GpioPadPort_J = 9, - GpioPadPort_K = 10, - GpioPadPort_L = 11, - GpioPadPort_M = 12, - GpioPadPort_N = 13, - GpioPadPort_O = 14, - GpioPadPort_P = 15, - GpioPadPort_Q = 16, - GpioPadPort_R = 17, - GpioPadPort_S = 18, - GpioPadPort_T = 19, - GpioPadPort_U = 20, - GpioPadPort_V = 21, - GpioPadPort_W = 22, - GpioPadPort_X = 23, - GpioPadPort_Y = 24, - GpioPadPort_Z = 25, - GpioPadPort_AA = 26, - GpioPadPort_BB = 27, - GpioPadPort_CC = 28, - GpioPadPort_DD = 29, - GpioPadPort_EE = 30, - GpioPadPort_FF = 31, - GpioPadPort_Count = 32, - }; - - using InternalGpioPadNumber = int; - - constexpr unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { - AMS_ASSERT(which < 8); - - return (static_cast(port) * 8) + which; - } - - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_None = -1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_0 = 0x00; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_1 = 0x01; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_2 = 0x02; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_3 = 0x03; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_4 = 0x04; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_5 = 0x05; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_6 = 0x06; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_7 = 0x07; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_0 = 0x08; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_1 = 0x09; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_2 = 0x0A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_3 = 0x0B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_4 = 0x0C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_5 = 0x0D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_6 = 0x0E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_7 = 0x0F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_0 = 0x10; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_1 = 0x11; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_2 = 0x12; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_3 = 0x13; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_4 = 0x14; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_5 = 0x15; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_6 = 0x16; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_7 = 0x17; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_0 = 0x18; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_1 = 0x19; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_2 = 0x1A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_3 = 0x1B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_4 = 0x1C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_5 = 0x1D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_6 = 0x1E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_7 = 0x1F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_0 = 0x20; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_1 = 0x21; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_2 = 0x22; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_3 = 0x23; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_4 = 0x24; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_5 = 0x25; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_6 = 0x26; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_7 = 0x27; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_0 = 0x28; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_1 = 0x29; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_2 = 0x2A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_3 = 0x2B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_4 = 0x2C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_5 = 0x2D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_6 = 0x2E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_7 = 0x2F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_0 = 0x30; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_1 = 0x31; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_2 = 0x32; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_3 = 0x33; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_4 = 0x34; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_5 = 0x35; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_6 = 0x36; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_7 = 0x37; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_0 = 0x38; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_1 = 0x39; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_2 = 0x3A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_3 = 0x3B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_4 = 0x3C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_5 = 0x3D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_6 = 0x3E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_7 = 0x3F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_0 = 0x40; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_1 = 0x41; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_2 = 0x42; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_3 = 0x43; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_4 = 0x44; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_5 = 0x45; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_6 = 0x46; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_7 = 0x47; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_0 = 0x48; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_1 = 0x49; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_2 = 0x4A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_3 = 0x4B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_4 = 0x4C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_5 = 0x4D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_6 = 0x4E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_7 = 0x4F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_0 = 0x50; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_1 = 0x51; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_2 = 0x52; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_3 = 0x53; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_4 = 0x54; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_5 = 0x55; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_6 = 0x56; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_7 = 0x57; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_0 = 0x58; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_1 = 0x59; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_2 = 0x5A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_3 = 0x5B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_4 = 0x5C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_5 = 0x5D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_6 = 0x5E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_7 = 0x5F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_0 = 0x60; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_1 = 0x61; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_2 = 0x62; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_3 = 0x63; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_4 = 0x64; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_5 = 0x65; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_6 = 0x66; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_7 = 0x67; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_0 = 0x68; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_1 = 0x69; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_2 = 0x6A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_3 = 0x6B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_4 = 0x6C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_5 = 0x6D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_6 = 0x6E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_7 = 0x6F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_0 = 0x70; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_1 = 0x71; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_2 = 0x72; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_3 = 0x73; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_4 = 0x74; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_5 = 0x75; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_6 = 0x76; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_7 = 0x77; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_0 = 0x78; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_1 = 0x79; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_2 = 0x7A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_3 = 0x7B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_4 = 0x7C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_5 = 0x7D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_6 = 0x7E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_7 = 0x7F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_0 = 0x80; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_1 = 0x81; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_2 = 0x82; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_3 = 0x83; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_4 = 0x84; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_5 = 0x85; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_6 = 0x86; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_7 = 0x87; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_0 = 0x88; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_1 = 0x89; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_2 = 0x8A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_3 = 0x8B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_4 = 0x8C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_5 = 0x8D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_6 = 0x8E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_7 = 0x8F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_0 = 0x90; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_1 = 0x91; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_2 = 0x92; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_3 = 0x93; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_4 = 0x94; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_5 = 0x95; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_6 = 0x96; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_7 = 0x97; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_0 = 0x98; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_1 = 0x99; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_2 = 0x9A; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_3 = 0x9B; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_4 = 0x9C; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_5 = 0x9D; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_6 = 0x9E; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_7 = 0x9F; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_0 = 0xA0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_1 = 0xA1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_2 = 0xA2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_3 = 0xA3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_4 = 0xA4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_5 = 0xA5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_6 = 0xA6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_7 = 0xA7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_0 = 0xA8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_1 = 0xA9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_2 = 0xAA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_3 = 0xAB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_4 = 0xAC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_5 = 0xAD; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_6 = 0xAE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_7 = 0xAF; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_0 = 0xB0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_1 = 0xB1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_2 = 0xB2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_3 = 0xB3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_4 = 0xB4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_5 = 0xB5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_6 = 0xB6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_7 = 0xB7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_0 = 0xB8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_1 = 0xB9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_2 = 0xBA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_3 = 0xBB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_4 = 0xBC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_5 = 0xBD; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_6 = 0xBE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_7 = 0xBF; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_0 = 0xC0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_1 = 0xC1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_2 = 0xC2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_3 = 0xC3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_4 = 0xC4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_5 = 0xC5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_6 = 0xC6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_7 = 0xC7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_0 = 0xC8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_1 = 0xC9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_2 = 0xCA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_3 = 0xCB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_4 = 0xCC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_5 = 0xCD; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_6 = 0xCE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_7 = 0xCF; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_0 = 0xD0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_1 = 0xD1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_2 = 0xD2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_3 = 0xD3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_4 = 0xD4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_5 = 0xD5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_6 = 0xD6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_7 = 0xD7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_0 = 0xD8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_1 = 0xD9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_2 = 0xDA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_3 = 0xDB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_4 = 0xDC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_5 = 0xDD; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_6 = 0xDE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_7 = 0xDF; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_0 = 0xE0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_1 = 0xE1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_2 = 0xE2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_3 = 0xE3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_4 = 0xE4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_5 = 0xE5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_6 = 0xE6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_7 = 0xE7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_0 = 0xE8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_1 = 0xE9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_2 = 0xEA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_3 = 0xEB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_4 = 0xEC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_5 = 0xED; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_6 = 0xEE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_7 = 0xEF; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_0 = 0xF0; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_1 = 0xF1; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_2 = 0xF2; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_3 = 0xF3; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_4 = 0xF4; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_5 = 0xF5; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_6 = 0xF6; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_7 = 0xF7; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_0 = 0xF8; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_1 = 0xF9; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_2 = 0xFA; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_3 = 0xFB; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_4 = 0xFC; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_5 = 0xFD; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_6 = 0xFE; - constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_7 = 0xFF; - - struct PadMapCombination { - DeviceCode device_code; - InternalGpioPadNumber internal_number; - wec::WakeEvent wake_event; - }; - - #include "gpio_internal_pad_map_combination.inc" - - struct PadInfo { - wec::WakeEvent wake_event; - - constexpr PadInfo() : wake_event(wec::WakeEvent_None) { /* ... */ } - - constexpr explicit PadInfo(wec::WakeEvent we) : wake_event(we) { /* ... */ } - - constexpr bool operator ==(const PadInfo &rhs) const { return this->wake_event == rhs.wake_event; } - constexpr bool operator !=(const PadInfo &rhs) const { return !(*this == rhs); } - }; - - struct PadStatus { - bool is_wake_active; - bool is_wake_active_debug; - - constexpr PadStatus() : is_wake_active(false), is_wake_active_debug(false) { /* ... */ } - }; - - class TegraPad : public ::ams::gpio::driver::Pad { - AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::TegraPad, ::ams::gpio::driver::Pad); - private: - using Base = ::ams::gpio::driver::Pad; - private: - util::IntrusiveListNode interrupt_list_node; - PadInfo info; - PadStatus status; - public: - using InterruptListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&TegraPad::interrupt_list_node>; - using InterruptList = typename InterruptListTraits::ListType; - friend class util::IntrusiveList>; - public: - TegraPad() : Pad(), interrupt_list_node(), info(), status() { /* ... */ } - - const PadInfo &GetInfo() const { return this->info; } - PadStatus &GetStatus() { return this->status; } - - void SetParameters(int pad, const PadInfo &i) { - Base::SetPadNumber(pad); - this->info = info; - } - - bool IsLinkedToInterruptBoundPadList() const { - return this->interrupt_list_node.IsLinked(); - } - }; - -} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp deleted file mode 100644 index 9b25143..0000000 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::gpio::driver::board::nintendo_nx::impl { - - struct WakePinConfig { - wec::WakeEvent wake_event; - bool enable; - wec::WakeEventLevel level; - }; - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_bus_device_map.inc b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_bus_device_map.inc new file mode 100644 index 0000000..3017020 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_bus_device_map.inc @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by i2cgen.py, do not edit manually. */ + +constexpr inline const I2cDeviceDefinition I2c1DeviceList[] = { + { DeviceCode_ClassicController, 0x52 }, + { DeviceCode_Nct72, 0x4C }, + { DeviceCode_Alc5639, 0x1C }, + { DeviceCode_Bq24193, 0x6B }, + { DeviceCode_Max17050, 0x36 }, + { DeviceCode_Bm92t30mwv, 0x18 }, +}; + +constexpr inline const I2cDeviceDefinition I2c2DeviceList[] = { + { DeviceCode_Ina226Vdd15v0Hb, 0x40 }, + { DeviceCode_Ina226VsysCpuDs, 0x41 }, + { DeviceCode_Ina226VsysGpuDs, 0x44 }, + { DeviceCode_Ina226VsysDdrDs, 0x45 }, + { DeviceCode_Ina226VsysAp, 0x46 }, + { DeviceCode_Ina226VsysBlDs, 0x47 }, + { DeviceCode_Bh1730, 0x29 }, + { DeviceCode_Ina226VsysCore, 0x48 }, + { DeviceCode_Ina226Soc1V8, 0x49 }, + { DeviceCode_Ina226Lpddr1V8, 0x4A }, + { DeviceCode_Ina226Reg1V32, 0x4B }, + { DeviceCode_Ina226Vdd3V3Sys, 0x4D }, + { DeviceCode_Ina226VddDdr0V6, 0x4E }, + { DeviceCode_HoagNfcIc, 0x08 }, +}; + +constexpr inline const I2cDeviceDefinition I2c3DeviceList[] = { + { DeviceCode_Ftm3bd56, 0x49 }, +}; + +constexpr inline const I2cDeviceDefinition I2c4DeviceList[] = { + { DeviceCode_HdmiDdc, 0x50 }, + { DeviceCode_HdmiScdc, 0x54 }, + { DeviceCode_HdmiHdcp, 0x3A }, +}; + +constexpr inline const I2cDeviceDefinition I2c5DeviceList[] = { + { DeviceCode_Max77620Rtc, 0x68 }, + { DeviceCode_Max77620Pmic, 0x3C }, + { DeviceCode_Max77621Cpu, 0x1B }, + { DeviceCode_Max77621Gpu, 0x1C }, + { DeviceCode_Fan53528, 0x52 }, + { DeviceCode_Max77812_3, 0x31 }, + { DeviceCode_Max77812_2, 0x33 }, +}; + +constexpr inline const I2cBusDefinition I2cBusList[] = { + { DeviceCode_I2c1, 0x7000C000, 0x100, SpeedMode_Standard, 70, I2c1DeviceList, util::size(I2c1DeviceList) }, + { DeviceCode_I2c2, 0x7000C400, 0x100, SpeedMode_Fast, 116, I2c2DeviceList, util::size(I2c2DeviceList) }, + { DeviceCode_I2c3, 0x7000C500, 0x100, SpeedMode_Fast, 124, I2c3DeviceList, util::size(I2c3DeviceList) }, + { DeviceCode_I2c4, 0x7000C700, 0x100, SpeedMode_Standard, 152, I2c4DeviceList, util::size(I2c4DeviceList) }, + { DeviceCode_I2c5, 0x7000D000, 0x100, SpeedMode_Fast, 85, I2c5DeviceList, util::size(I2c5DeviceList) }, +}; diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_driver_api.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_driver_api.cpp new file mode 100644 index 0000000..bab1817 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/i2c_driver_api.cpp @@ -0,0 +1,108 @@ +/* + * 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 "impl/i2c_bus_manager.hpp" +#include "impl/i2c_device_property_manager.hpp" + +namespace ams::i2c::driver::board::nintendo_nx { + + namespace { + + struct I2cDeviceDefinition { + DeviceCode device_code; + u8 slave_address; + }; + + struct I2cBusDefinition { + DeviceCode device_code; + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + SpeedMode speed_mode; + os::InterruptName interrupt_name; + const I2cDeviceDefinition *devices; + size_t num_devices; + + constexpr bool IsPowerBus() const { + return this->device_code == DeviceCode_I2c5; + } + }; + + #include "i2c_bus_device_map.inc" + + void CheckSpeedMode(SpeedMode speed_mode) { + switch (speed_mode) { + case SpeedMode_Standard: break; + case SpeedMode_Fast: break; + case SpeedMode_FastPlus: break; + case SpeedMode_HighSpeed: break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void Initialize(impl::I2cBusAccessorManager &bus_manager, impl::I2cDevicePropertyManager &device_manager) { + /* Create an accessor for each bus. */ + for (const auto &bus_def : I2cBusList) { + /* Check that the speed mode is valid. */ + CheckSpeedMode(bus_def.speed_mode); + + /* Find the bus. */ + auto *bus = bus_manager.Find([&bus_def](const auto &it) { + return it.GetRegistersPhysicalAddress() == bus_def.registers_phys_addr; + }); + + /* If the bus doesn't exist, create it. */ + if (bus == nullptr) { + /* Allocate the bus. */ + bus = bus_manager.Allocate(); + + /* Initialize the bus. */ + bus->Initialize(bus_def.registers_phys_addr, bus_def.registers_size, bus_def.interrupt_name, bus_def.IsPowerBus(), bus_def.speed_mode); + + /* Register the bus. */ + i2c::driver::RegisterDriver(bus); + } + + /* Set the bus's device code. */ + bus->RegisterDeviceCode(bus_def.device_code); + + /* Allocate and register the devices for the bus. */ + for (size_t i = 0; i < bus_def.num_devices; ++i) { + /* Get the device definition. */ + const auto &entry = bus_def.devices[i]; + + /* Allocate the device. */ + I2cDeviceProperty *device = device_manager.Allocate(entry.slave_address, AddressingMode_SevenBit); + + /* Register the device with our bus. */ + bus->RegisterDevice(device); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(i2c::driver::RegisterDeviceCode(entry.device_code, device)); + } + } + } + + } + + void Initialize() { + /* TODO: Should these be moved into getters? They're only used here, and they never destruct. */ + static impl::I2cBusAccessorManager s_bus_accessor_manager(ddsf::GetMemoryResource()); + static impl::I2cDevicePropertyManager s_device_manager(ddsf::GetMemoryResource()); + + return Initialize(s_bus_accessor_manager, s_device_manager); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.cpp new file mode 100644 index 0000000..5e8dff8 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.cpp @@ -0,0 +1,771 @@ +/* + * 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 "i2c_bus_accessor.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline TimeSpan Timeout = TimeSpan::FromMilliSeconds(100); + + #define IO_PACKET_BITS_MASK(NAME) REG_NAMED_BITS_MASK (_IMPL_IO_PACKET_, NAME) + #define IO_PACKET_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (_IMPL_IO_PACKET_, NAME, VALUE) + #define IO_PACKET_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (_IMPL_IO_PACKET_, NAME, ENUM) + #define IO_PACKET_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(_IMPL_IO_PACKET_, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_IO_PACKET_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (_IMPL_IO_PACKET_, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_IO_PACKET_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_IO_PACKET_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 (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(HEADER_WORD0_PKT_TYPE, 0, REQUEST, RESPONSE, INTERRUPT, STOP, RSVD4, RSVD5, RSVD6, RSVD7); + DEFINE_IO_PACKET_REG_FOUR_BIT_ENUM(HEADER_WORD0_PROTOCOL, 4, RSVD0, I2C, RSVD2, RSVD3, RSVD4, RSVD5, RSVD6, RSVD7, RSVD8, RSVD9, RSVD10, RSVD11, RSVD12, RSVD13, RSVD14, RSVD15) + DEFINE_IO_PACKET_REG(HEADER_WORD0_CONTROLLER_ID, 12, 4); + DEFINE_IO_PACKET_REG(HEADER_WORD0_PKT_ID, 16, 8); + DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(HEADER_WORD0_PROT_HDR_SZ, 28, 1_WORD, 2_WORD, 3_WORD, 4_WORD); + + DEFINE_IO_PACKET_REG(HEADER_WORD1_PAYLOAD_SIZE, 0, 12); + + DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_SLAVE_ADDR, 0, 10); + DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_HS_MASTER_ADDR, 12, 3); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_XFER, 15, USE_REPEAT_START_TOP, CONTINUE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_REPEAT_START_STOP, 16, STOP_CONDITION, REPEAT_START_CONDITION); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_IE, 17, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_ADDRESS_MODE, 18, SEVEN_BIT, TEN_BIT); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_READ_WRITE, 19, WRITE, READ); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_SEND_START_BYTE, 20, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_ON_NACK, 21, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_HS_MODE, 22, DISABLE, ENABLE); + + } + + void I2cBusAccessor::Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm) { + AMS_ASSERT(this->state == State::NotInitialized); + + this->is_power_bus = pb; + this->speed_mode = sm; + this->interrupt_name = intr; + this->registers_phys_addr = reg_paddr; + this->registers_size = reg_size; + this->state = State::Initializing; + } + + void I2cBusAccessor::RegisterDeviceCode(DeviceCode dc) { + AMS_ASSERT(this->state == State::Initializing); + + this->device_code = dc; + } + + void I2cBusAccessor::InitializeDriver() { + AMS_ASSERT(this->state == State::Initializing); + + this->registers = reinterpret_cast(dd::QueryIoMapping(this->registers_phys_addr, this->registers_size)); + AMS_ABORT_UNLESS(this->registers != nullptr); + + this->state = State::Initialized; + } + + void I2cBusAccessor::FinalizeDriver() { + AMS_ASSERT(this->state == State::Initialized); + this->state = State::Initializing; + } + + Result I2cBusAccessor::InitializeDevice(I2cDeviceProperty *device) { + /* Check that the device is valid. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* Increment our user count -- if we're already open, we're done. */ + AMS_ASSERT(this->user_count >= 0); + ++this->user_count; + R_SUCCEED_IF(this->user_count > 1); + + /* Initialize our interrupt event. */ + os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + + /* If we're not power bus, perform power management init. */ + if (!this->is_power_bus) { + /* Initialize regulator library. */ + regulator::Initialize(); + + /* Try to open regulator session. */ + R_TRY(this->TryOpenRegulatorSession()); + + /* If we have a regulator session, set voltage to 2.9V. */ + if (this->has_regulator_session) { + /* NOTE: Nintendo does not check the result, here. */ + regulator::SetVoltageValue(std::addressof(this->regulator_session), 2'900'000u); + } + + /* Initialize clock/reset library. */ + clkrst::Initialize(); + } + + /* Execute initial config. */ + this->ExecuteInitialConfig(); + + /* If we have a regulator session, enable voltage. */ + if (!this->is_power_bus && this->has_regulator_session) { + /* Check whether voltage was already enabled. */ + const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); + + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); + + /* If we enabled voltage, delay to give our enable time to take. */ + if (!was_enabled) { + os::SleepThread(TimeSpan::FromMicroSeconds(560)); + } + } + + return ResultSuccess(); + } + + void I2cBusAccessor::FinalizeDevice(I2cDeviceProperty *device) { + /* Check that the device is valid. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* Increment our user count -- if we're not the last user, we're done. */ + AMS_ASSERT(this->user_count > 0); + --this->user_count; + if (this->user_count > 0) { + return; + } + + /* Finalize our interrupt event. */ + os::FinalizeInterruptEvent(std::addressof(this->interrupt_event)); + + /* If we have a regulator session, disable voltage. */ + if (this->has_regulator_session) { + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); + } + + /* Finalize the clock/reset library. */ + clkrst::Finalize(); + + /* If we have a regulator session, close it. */ + if (this->has_regulator_session) { + regulator::CloseSession(std::addressof(this->regulator_session)); + this->has_regulator_session = false; + } + + /* Finalize the regulator library. */ + regulator::Finalize(); + } + + Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(src != nullptr); + AMS_ASSERT(src_size > 0); + + if (this->is_power_bus) { + AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); + } else { + AMS_ASSERT(this->state == State::Initialized); + } + + /* Send the data. */ + return this->Send(static_cast(src), src_size, option, device->GetAddress(), device->GetAddressingMode()); + } + + Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size > 0); + + if (this->is_power_bus) { + AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); + } else { + AMS_ASSERT(this->state == State::Initialized); + } + + /* Send the data. */ + return this->Receive(static_cast(dst), dst_size, option, device->GetAddress(), device->GetAddressingMode()); + } + + void I2cBusAccessor::SuspendBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, disable clock/voltage appropriately. */ + if (!this->is_power_bus && this->user_count > 0) { + /* Disable clock. */ + { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Set clock disabled for the session. */ + clkrst::SetClockDisabled(std::addressof(clkrst_session)); + } + + /* Disable voltage. */ + if (this->has_regulator_session) { + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); + } + } + + /* Update state. */ + this->state = State::Suspended; + } + + void I2cBusAccessor::SuspendPowerBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Suspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, disable clock/voltage appropriately. */ + if (this->is_power_bus && this->user_count > 0) { + /* Nothing should actually be done here. */ + } + + /* Update state. */ + this->state = State::PowerBusSuspended; + } + + void I2cBusAccessor::ResumeBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Suspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, enable clock/voltage appropriately. */ + if (!this->is_power_bus && this->user_count > 0) { + /* Enable voltage. */ + if (this->has_regulator_session) { + /* Check whether voltage was already enabled. */ + const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); + + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); + + /* If we enabled voltage, delay to give our enable time to take. */ + if (!was_enabled) { + os::SleepThread(TimeSpan::FromMicroSeconds(560)); + } + } + + /* Execute initial config, which will enable clock as relevant. */ + this->ExecuteInitialConfig(); + } + + /* Update state. */ + this->state = State::Initialized; + } + + void I2cBusAccessor::ResumePowerBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::PowerBusSuspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, enable clock/voltage appropriately. */ + if (this->is_power_bus && this->user_count > 0) { + /* Execute initial config, which will enable clock as relevant. */ + this->ExecuteInitialConfig(); + } + + /* Update state. */ + this->state = State::Suspended; + } + + Result I2cBusAccessor::TryOpenRegulatorSession() { + /* Ensure we track the session. */ + this->has_regulator_session = true; + auto s_guard = SCOPE_GUARD { this->has_regulator_session = false; }; + + /* Try to open the session. */ + R_TRY_CATCH(regulator::OpenSession(std::addressof(this->regulator_session), this->device_code)) { + R_CATCH(ddsf::ResultDeviceCodeNotFound) { + /* It's okay if the device isn't found, but we don't have a session if so. */ + this->has_regulator_session = false; + } + } R_END_TRY_CATCH; + + /* We opened (or not). */ + s_guard.Cancel(); + return ResultSuccess(); + } + + void I2cBusAccessor::ExecuteInitialConfig() { + /* Lock exclusive access to registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Reset the controller. */ + this->ResetController(); + + /* Set clock registers. */ + this->SetClockRegisters(this->speed_mode); + + /* Set packet mode registers. */ + this->SetPacketModeRegisters(); + + /* Flush fifos. */ + this->FlushFifos(); + } + + Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Configure interrupt mask, clear interrupt status. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); + + /* Write the header. */ + this->WriteHeader(Xfer_Write, src_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + const u8 *cur = src; + size_t remaining = src_size; + + while (true) { + /* Get the number of empty bytes in the fifo status. */ + const u32 empty = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_TX_FIFO_EMPTY_CNT)); + + /* Write up to (empty) bytes to the fifo. */ + for (u32 i = 0; remaining > 0 && i < empty; ++i) { + /* Build the data word to send. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + + u32 word = 0; + for (size_t j = 0; j < cur_bytes; ++j) { + word |= cur[j] << (BITSIZEOF(u8) * j); + } + + /* Write the data word. */ + reg::Write(this->registers->tx_packet_fifo, word); + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + + /* If we're done, break. */ + if (remaining == 0) { + break; + } + + /* Wait for our current data to send. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + } + + /* Configure interrupt mask to not care about tfifo data req. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + /* Wait for the packet transfer to complete. */ + while (true) { + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* Check if packet transfer is done. */ + if (reg::HasValue(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET))) { + break; + } + + /* Wait for our the packet to transfer. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* We're done. */ + this->DisableInterruptMask(); + return ResultSuccess(); + } + + Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Configure interrupt mask, clear interrupt status. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); + + /* Write the header. */ + this->WriteHeader(Xfer_Read, dst_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + u8 *cur = dst; + size_t remaining = dst_size; + + while (remaining > 0) { + /* Wait for data to come in. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* Get the number of full bytes in the fifo status. */ + const u32 full = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_RX_FIFO_FULL_CNT)); + + /* Determine how many words we can read. */ + const size_t cur_words = std::min(util::DivideUp(remaining, sizeof(u32)), static_cast(full)); + + /* Read the correct number of words from the fifo. */ + for (size_t i = 0; i < cur_words; ++i) { + /* Read the word from the fifo. */ + const u32 word = reg::Read(this->registers->rx_fifo); + + /* Copy bytes from the word. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + for (size_t j = 0; j < cur_bytes; ++j) { + cur[j] = (word >> (BITSIZEOF(u8) * j)) & 0xFF; + } + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + } + + /* We're done. */ + return ResultSuccess(); + } + + void I2cBusAccessor::WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Parse interesting values from our arguments. */ + const bool is_read = xfer == Xfer_Read; + const bool is_7_bit = addressing_mode == AddressingMode_SevenBit; + const bool is_stop = (option & TransactionOption_StopCondition) != 0; + const bool is_hs = this->speed_mode == SpeedMode_HighSpeed; + const u32 slave_addr = ((static_cast(slave_address) & 0x7F) << 1) | (is_read ? 1 : 0); + + /* Flush fifos. */ + this->FlushFifos(); + + /* Enqueue the first header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM (HEADER_WORD0_PROT_HDR_SZ, 1_WORD), + IO_PACKET_BITS_VALUE(HEADER_WORD0_PKT_ID, 0), + IO_PACKET_BITS_VALUE(HEADER_WORD0_CONTROLLER_ID, 0), + IO_PACKET_BITS_ENUM (HEADER_WORD0_PROTOCOL, I2C), + IO_PACKET_BITS_ENUM (HEADER_WORD0_PKT_TYPE, REQUEST)); + + /* Enqueue the second header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_VALUE(HEADER_WORD1_PAYLOAD_SIZE, static_cast(size - 1))); + + /* Enqueue the protocol header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_HS_MODE, is_hs, ENABLE, DISABLE), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_ON_NACK, DISABLE), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_SEND_START_BYTE, DISABLE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_READ_WRITE, is_read, READ, WRITE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_ADDRESS_MODE, is_7_bit, SEVEN_BIT, TEN_BIT), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_IE, ENABLE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_REPEAT_START_STOP, is_stop, STOP_CONDITION, REPEAT_START_CONDITION), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_XFER, USE_REPEAT_START_TOP), + IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_HS_MASTER_ADDR, 0), + IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_SLAVE_ADDR, slave_addr)); + } + + void I2cBusAccessor::ResetController() const { + /* Reset the controller. */ + if (!this->is_power_bus) { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Reset the controller, setting clock rate to 408 MHz / 5 (to account for clock divisor). */ + /* NOTE: Nintendo does not check result for any of these calls. */ + clkrst::SetResetAsserted(std::addressof(clkrst_session)); + clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (4 + 1)); + clkrst::SetResetDeasserted(std::addressof(clkrst_session)); + } + } + + void I2cBusAccessor::ClearBus() const { + /* Try to clear the bus up to three times. */ + constexpr int MaxRetryCount = 3; + constexpr int BusyLoopMicroSeconds = 1000; + + int try_count = 0; + bool need_retry; + do { + /* Update trackers. */ + ++try_count; + need_retry = false; + + /* Reset the controller. */ + this->ResetController(); + + /* Configure the sclk threshold for bus clear config. */ + reg::Write(this->registers->bus_clear_config, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9)); + + /* Set stop cond and terminate in bus clear config. */ + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, STOP)); + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE)); + + /* Set master config load, busy loop up to 1ms for it to take. */ + reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + const os::Tick start_tick_a = os::GetSystemTick(); + while (reg::HasValue(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE))) { + if ((os::GetSystemTick() - start_tick_a).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + + /* Set bus clear enable, wait up to 1ms for it to take. */ + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + const os::Tick start_tick_b = os::GetSystemTick(); + while (reg::HasValue(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE))) { + if ((os::GetSystemTick() - start_tick_b).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + + /* Wait up to 1ms for the bus clear to complete. */ + const os::Tick start_tick_c = os::GetSystemTick(); + while (reg::HasValue(this->registers->bus_clear_status, I2C_REG_BITS_ENUM(BUS_CLEAR_STATUS_BC_STATUS, NOT_CLEARED))) { + if ((os::GetSystemTick() - start_tick_c).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + } while (try_count < MaxRetryCount && need_retry); + } + + void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) { + /* Determine parameters for the speed mode. */ + u32 t_high, t_low, clk_div, debounce, src_div; + bool high_speed = false; + + if (this->is_power_bus) { + t_high = 0x02; + t_low = 0x04; + clk_div = 0x05; + debounce = 0x02; + src_div = 0; /* unused */ + } else { + switch (speed_mode) { + case SpeedMode_Standard: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x19; + debounce = 0x02; + src_div = 0x13; + break; + case SpeedMode_Fast: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x19; + debounce = 0x02; + src_div = 0x04; + break; + case SpeedMode_FastPlus: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x10; + debounce = 0x00; + src_div = 0x02; + break; + case SpeedMode_HighSpeed: + t_high = 0x03; + t_low = 0x08; + clk_div = 0x02; + debounce = 0x00; + src_div = 0x02; + high_speed = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + /* Write the clock divisors. */ + if (high_speed) { + reg::Write(this->registers->hs_interface_timing_0, I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_THIGH, t_high), + I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_TLOW, t_low)); + + reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, clk_div)); + } else { + reg::Write(this->registers->interface_timing_0, I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_THIGH, t_high), + I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_TLOW, t_low)); + + reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, clk_div)); + } + + /* Configure debounce. */ + reg::Write(this->registers->cnfg, I2C_REG_BITS_VALUE(I2C_CNFG_DEBOUNCE_CNT, debounce)); + reg::Read(this->registers->cnfg); + + /* Set the clock rate, if we should. */ + if (!this->is_power_bus) { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Reset the controller, setting clock rate to 408 MHz / (src_div + 1). */ + /* NOTE: Nintendo does not check result for any of these calls. */ + clkrst::SetResetAsserted(std::addressof(clkrst_session)); + clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (src_div + 1)); + clkrst::SetResetDeasserted(std::addressof(clkrst_session)); + } + } + + void I2cBusAccessor::SetPacketModeRegisters() { + /* Set packet mode enable. */ + reg::ReadWrite(this->registers->cnfg, I2C_REG_BITS_ENUM(I2C_CNFG_PACKET_MODE_EN, GO)); + + /* Set master config load. */ + reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Set tx/fifo triggers to default (maximum values). */ + reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), + I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7)); + } + + Result I2cBusAccessor::FlushFifos() { + /* Flush the fifo. */ + reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), + I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7), + I2C_REG_BITS_ENUM (FIFO_CONTROL_RX_FIFO_FLUSH, SET), + I2C_REG_BITS_ENUM (FIFO_CONTROL_TX_FIFO_FLUSH, SET)); + + /* Wait up to 5 ms for the flush to complete. */ + int count = 0; + while (!reg::HasValue(this->registers->fifo_control, I2C_REG_BITS_ENUM(FIFO_CONTROL_FIFO_FLUSH, RX_UNSET_TX_UNSET))) { + R_UNLESS((++count < 5), i2c::ResultBusBusy()); + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return ResultSuccess(); + } + + Result I2cBusAccessor::GetTransactionResult() const { + /* Get packet status/interrupt status. */ + volatile u32 packet_status = reg::Read(this->registers->packet_transfer_status); + volatile u32 interrupt_status = reg::Read(this->registers->interrupt_status_register); + + /* Check for ack. */ + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, UNSET)), i2c::ResultNoAck()); + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, UNSET)), i2c::ResultNoAck()); + R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, UNSET)), i2c::ResultNoAck()); + + /* If we lost arbitration, we'll need to clear the bus. */ + auto clear_guard = SCOPE_GUARD { this->ClearBus(); }; + + /* Check for arb lost. */ + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, UNSET)), i2c::ResultBusBusy()); + R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, UNSET)), i2c::ResultBusBusy()); + + clear_guard.Cancel(); + return ResultSuccess(); + } + + void I2cBusAccessor::HandleTransactionError(Result result) { + R_TRY_CATCH(result) { + R_CATCH(i2c::ResultNoAck, i2c::ResultBusBusy) { + /* Reset the controller. */ + this->ResetController(); + + /* Set clock registers. */ + this->SetClockRegisters(this->speed_mode); + + /* Set packet mode registers. */ + this->SetPacketModeRegisters(); + + /* Flush fifos. */ + this->FlushFifos(); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.hpp new file mode 100644 index 0000000..8691dba --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_accessor.hpp @@ -0,0 +1,137 @@ +/* + * 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 . + */ +#pragma once +#include +#include "i2c_i2c_registers.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cBusAccessor : public ::ams::i2c::driver::II2cDriver { + NON_COPYABLE(I2cBusAccessor); + NON_MOVEABLE(I2cBusAccessor); + AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::board::nintendo_nx::impl::I2cBusAccessor, ::ams::i2c::driver::II2cDriver); + private: + enum class State { + NotInitialized = 0, + Initializing = 1, + Initialized = 2, + Suspended = 3, + PowerBusSuspended = 4, + }; + + enum Xfer { + Xfer_Write = 0, + Xfer_Read = 1, + }; + private: + volatile I2cRegisters *registers; + SpeedMode speed_mode; + os::InterruptEventType interrupt_event; + int user_count; + os::SdkMutex user_count_mutex; + os::SdkMutex register_mutex; + regulator::RegulatorSession regulator_session; + bool has_regulator_session; + State state; + os::SdkMutex transaction_order_mutex; + bool is_power_bus; + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + os::InterruptName interrupt_name; + DeviceCode device_code; + util::IntrusiveListNode bus_accessor_list_node; + public: + using BusAccessorListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&I2cBusAccessor::bus_accessor_list_node>; + using BusAccessorList = typename BusAccessorListTraits::ListType; + friend class util::IntrusiveList>; + public: + I2cBusAccessor() + : registers(nullptr), speed_mode(SpeedMode_Fast), user_count(0), user_count_mutex(), + register_mutex(), has_regulator_session(false), state(State::NotInitialized), transaction_order_mutex(), + is_power_bus(false), registers_phys_addr(0), registers_size(0), interrupt_name(), device_code(-1), bus_accessor_list_node() + { + /* ... */ + } + + void Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm); + void RegisterDeviceCode(DeviceCode device_code); + + SpeedMode GetSpeedMode() const { return this->speed_mode; } + dd::PhysicalAddress GetRegistersPhysicalAddress() const { return this->registers_phys_addr; } + size_t GetRegistersSize() const { return this->registers_size; } + os::InterruptName GetInterruptName() const { return this->interrupt_name; } + private: + Result TryOpenRegulatorSession(); + + void ExecuteInitialConfig(); + + Result Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + Result Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + + void WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + + void ResetController() const; + void ClearBus() const; + void SetClockRegisters(SpeedMode speed_mode); + void SetPacketModeRegisters(); + + Result FlushFifos(); + + Result GetTransactionResult() const; + void HandleTransactionError(Result result); + + void DisableInterruptMask() { + reg::Write(this->registers->interrupt_mask_register, 0); + reg::Read(this->registers->interrupt_mask_register); + } + + Result CheckAndHandleError() { + const Result result = this->GetTransactionResult(); + this->HandleTransactionError(result); + if (R_FAILED(result)) { + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return result; + } + + return ResultSuccess(); + } + public: + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializeDevice(I2cDeviceProperty *device) override; + virtual void FinalizeDevice(I2cDeviceProperty *device) override; + + virtual Result Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) override; + virtual Result Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) override; + + virtual os::SdkMutex &GetTransactionOrderMutex() override { + return this->transaction_order_mutex; + } + + virtual void SuspendBus() override; + virtual void SuspendPowerBus() override; + + virtual void ResumeBus() override; + virtual void ResumePowerBus() override; + + virtual const DeviceCode &GetDeviceCode() const override { + return this->device_code; + } + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_manager.hpp new file mode 100644 index 0000000..64ae466 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_bus_manager.hpp @@ -0,0 +1,27 @@ +/* + * 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 . + */ +#pragma once +#include +#include "i2c_bus_accessor.hpp" +#include "i2c_i_allocator.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cBusAccessorManager : public IAllocator { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_device_property_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_device_property_manager.hpp new file mode 100644 index 0000000..726d876 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_device_property_manager.hpp @@ -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 . + */ +#pragma once +#include +#include "i2c_i_allocator.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cDevicePropertyManager : public IAllocator { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i2c_registers.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i2c_registers.hpp new file mode 100644 index 0000000..c8925ce --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i2c_registers.hpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + struct I2cRegisters { + volatile u32 cnfg; + volatile u32 cmd_addr0; + volatile u32 cmd_addr1; + volatile u32 cmd_data1; + volatile u32 cmd_data2; + volatile u32 _14; + volatile u32 _18; + volatile u32 status; + volatile u32 sl_cnfg; + volatile u32 sl_rcvd; + volatile u32 sl_status; + volatile u32 sl_addr1; + volatile u32 sl_addr2; + volatile u32 tlow_sext; + volatile u32 _38; + volatile u32 sl_delay_count; + volatile u32 sl_int_mask; + volatile u32 sl_int_source; + volatile u32 sl_int_set; + volatile u32 _4c; + volatile u32 tx_packet_fifo; + volatile u32 rx_fifo; + volatile u32 packet_transfer_status; + volatile u32 fifo_control; + volatile u32 fifo_status; + volatile u32 interrupt_mask_register; + volatile u32 interrupt_status_register; + volatile u32 clk_divisor_register; + volatile u32 interrupt_source_register; + volatile u32 interrupt_set_register; + volatile u32 slv_tx_packet_fifo; + volatile u32 slv_rx_fifo; + volatile u32 slv_packet_status; + volatile u32 bus_clear_config; + volatile u32 bus_clear_status; + volatile u32 config_load; + volatile u32 _90; + volatile u32 interface_timing_0; + volatile u32 interface_timing_1; + volatile u32 hs_interface_timing_0; + volatile u32 hs_interface_timing_1; + }; + static_assert(sizeof(I2cRegisters) == 0xA4); + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp new file mode 100644 index 0000000..270c882 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp @@ -0,0 +1,79 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + template + class IAllocator { + private: + using T = typename ListType::value_type; + private: + ams::MemoryResource *memory_resource; + ListType list; + mutable os::SdkMutex list_lock; + public: + IAllocator(ams::MemoryResource *mr) : memory_resource(mr), list(), list_lock() { /* ... */ } + + ~IAllocator() { + std::scoped_lock lk(this->list_lock); + + /* Remove all entries. */ + auto it = this->list.begin(); + while (it != this->list.end()) { + T *obj = std::addressof(*it); + it = this->list.erase(it); + + obj->~T(); + this->memory_resource->Deallocate(obj, sizeof(T)); + } + } + + template + T *Allocate(Args &&...args) { + std::scoped_lock lk(this->list_lock); + + /* Allocate space for the object. */ + void *storage = this->memory_resource->Allocate(sizeof(T), alignof(T)); + AMS_ABORT_UNLESS(storage != nullptr); + + /* Construct the object. */ + T *t = new (static_cast(storage)) T(std::forward(args)...); + + /* Link the object into our list. */ + this->list.push_back(*t); + + return t; + } + + template + T *Find(F f) { + std::scoped_lock lk(this->list_lock); + + for (T &it : this->list) { + if (f(static_cast(it))) { + return std::addressof(it); + } + } + + return nullptr; + } + + /* TODO: Support free */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc deleted file mode 100644 index 3017020..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by i2cgen.py, do not edit manually. */ - -constexpr inline const I2cDeviceDefinition I2c1DeviceList[] = { - { DeviceCode_ClassicController, 0x52 }, - { DeviceCode_Nct72, 0x4C }, - { DeviceCode_Alc5639, 0x1C }, - { DeviceCode_Bq24193, 0x6B }, - { DeviceCode_Max17050, 0x36 }, - { DeviceCode_Bm92t30mwv, 0x18 }, -}; - -constexpr inline const I2cDeviceDefinition I2c2DeviceList[] = { - { DeviceCode_Ina226Vdd15v0Hb, 0x40 }, - { DeviceCode_Ina226VsysCpuDs, 0x41 }, - { DeviceCode_Ina226VsysGpuDs, 0x44 }, - { DeviceCode_Ina226VsysDdrDs, 0x45 }, - { DeviceCode_Ina226VsysAp, 0x46 }, - { DeviceCode_Ina226VsysBlDs, 0x47 }, - { DeviceCode_Bh1730, 0x29 }, - { DeviceCode_Ina226VsysCore, 0x48 }, - { DeviceCode_Ina226Soc1V8, 0x49 }, - { DeviceCode_Ina226Lpddr1V8, 0x4A }, - { DeviceCode_Ina226Reg1V32, 0x4B }, - { DeviceCode_Ina226Vdd3V3Sys, 0x4D }, - { DeviceCode_Ina226VddDdr0V6, 0x4E }, - { DeviceCode_HoagNfcIc, 0x08 }, -}; - -constexpr inline const I2cDeviceDefinition I2c3DeviceList[] = { - { DeviceCode_Ftm3bd56, 0x49 }, -}; - -constexpr inline const I2cDeviceDefinition I2c4DeviceList[] = { - { DeviceCode_HdmiDdc, 0x50 }, - { DeviceCode_HdmiScdc, 0x54 }, - { DeviceCode_HdmiHdcp, 0x3A }, -}; - -constexpr inline const I2cDeviceDefinition I2c5DeviceList[] = { - { DeviceCode_Max77620Rtc, 0x68 }, - { DeviceCode_Max77620Pmic, 0x3C }, - { DeviceCode_Max77621Cpu, 0x1B }, - { DeviceCode_Max77621Gpu, 0x1C }, - { DeviceCode_Fan53528, 0x52 }, - { DeviceCode_Max77812_3, 0x31 }, - { DeviceCode_Max77812_2, 0x33 }, -}; - -constexpr inline const I2cBusDefinition I2cBusList[] = { - { DeviceCode_I2c1, 0x7000C000, 0x100, SpeedMode_Standard, 70, I2c1DeviceList, util::size(I2c1DeviceList) }, - { DeviceCode_I2c2, 0x7000C400, 0x100, SpeedMode_Fast, 116, I2c2DeviceList, util::size(I2c2DeviceList) }, - { DeviceCode_I2c3, 0x7000C500, 0x100, SpeedMode_Fast, 124, I2c3DeviceList, util::size(I2c3DeviceList) }, - { DeviceCode_I2c4, 0x7000C700, 0x100, SpeedMode_Standard, 152, I2c4DeviceList, util::size(I2c4DeviceList) }, - { DeviceCode_I2c5, 0x7000D000, 0x100, SpeedMode_Fast, 85, I2c5DeviceList, util::size(I2c5DeviceList) }, -}; diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp deleted file mode 100644 index bab1817..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 "impl/i2c_bus_manager.hpp" -#include "impl/i2c_device_property_manager.hpp" - -namespace ams::i2c::driver::board::nintendo_nx { - - namespace { - - struct I2cDeviceDefinition { - DeviceCode device_code; - u8 slave_address; - }; - - struct I2cBusDefinition { - DeviceCode device_code; - dd::PhysicalAddress registers_phys_addr; - size_t registers_size; - SpeedMode speed_mode; - os::InterruptName interrupt_name; - const I2cDeviceDefinition *devices; - size_t num_devices; - - constexpr bool IsPowerBus() const { - return this->device_code == DeviceCode_I2c5; - } - }; - - #include "i2c_bus_device_map.inc" - - void CheckSpeedMode(SpeedMode speed_mode) { - switch (speed_mode) { - case SpeedMode_Standard: break; - case SpeedMode_Fast: break; - case SpeedMode_FastPlus: break; - case SpeedMode_HighSpeed: break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void Initialize(impl::I2cBusAccessorManager &bus_manager, impl::I2cDevicePropertyManager &device_manager) { - /* Create an accessor for each bus. */ - for (const auto &bus_def : I2cBusList) { - /* Check that the speed mode is valid. */ - CheckSpeedMode(bus_def.speed_mode); - - /* Find the bus. */ - auto *bus = bus_manager.Find([&bus_def](const auto &it) { - return it.GetRegistersPhysicalAddress() == bus_def.registers_phys_addr; - }); - - /* If the bus doesn't exist, create it. */ - if (bus == nullptr) { - /* Allocate the bus. */ - bus = bus_manager.Allocate(); - - /* Initialize the bus. */ - bus->Initialize(bus_def.registers_phys_addr, bus_def.registers_size, bus_def.interrupt_name, bus_def.IsPowerBus(), bus_def.speed_mode); - - /* Register the bus. */ - i2c::driver::RegisterDriver(bus); - } - - /* Set the bus's device code. */ - bus->RegisterDeviceCode(bus_def.device_code); - - /* Allocate and register the devices for the bus. */ - for (size_t i = 0; i < bus_def.num_devices; ++i) { - /* Get the device definition. */ - const auto &entry = bus_def.devices[i]; - - /* Allocate the device. */ - I2cDeviceProperty *device = device_manager.Allocate(entry.slave_address, AddressingMode_SevenBit); - - /* Register the device with our bus. */ - bus->RegisterDevice(device); - - /* Register the device code with our driver. */ - R_ABORT_UNLESS(i2c::driver::RegisterDeviceCode(entry.device_code, device)); - } - } - } - - } - - void Initialize() { - /* TODO: Should these be moved into getters? They're only used here, and they never destruct. */ - static impl::I2cBusAccessorManager s_bus_accessor_manager(ddsf::GetMemoryResource()); - static impl::I2cDevicePropertyManager s_device_manager(ddsf::GetMemoryResource()); - - return Initialize(s_bus_accessor_manager, s_device_manager); - } - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp deleted file mode 100644 index 5e8dff8..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp +++ /dev/null @@ -1,771 +0,0 @@ -/* - * 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 "i2c_bus_accessor.hpp" - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - namespace { - - constexpr inline TimeSpan Timeout = TimeSpan::FromMilliSeconds(100); - - #define IO_PACKET_BITS_MASK(NAME) REG_NAMED_BITS_MASK (_IMPL_IO_PACKET_, NAME) - #define IO_PACKET_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (_IMPL_IO_PACKET_, NAME, VALUE) - #define IO_PACKET_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (_IMPL_IO_PACKET_, NAME, ENUM) - #define IO_PACKET_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(_IMPL_IO_PACKET_, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) - - #define DEFINE_IO_PACKET_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (_IMPL_IO_PACKET_, NAME, __OFFSET__, __WIDTH__) - #define DEFINE_IO_PACKET_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE) - #define DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) - #define DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) - #define DEFINE_IO_PACKET_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 (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) - - DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(HEADER_WORD0_PKT_TYPE, 0, REQUEST, RESPONSE, INTERRUPT, STOP, RSVD4, RSVD5, RSVD6, RSVD7); - DEFINE_IO_PACKET_REG_FOUR_BIT_ENUM(HEADER_WORD0_PROTOCOL, 4, RSVD0, I2C, RSVD2, RSVD3, RSVD4, RSVD5, RSVD6, RSVD7, RSVD8, RSVD9, RSVD10, RSVD11, RSVD12, RSVD13, RSVD14, RSVD15) - DEFINE_IO_PACKET_REG(HEADER_WORD0_CONTROLLER_ID, 12, 4); - DEFINE_IO_PACKET_REG(HEADER_WORD0_PKT_ID, 16, 8); - DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(HEADER_WORD0_PROT_HDR_SZ, 28, 1_WORD, 2_WORD, 3_WORD, 4_WORD); - - DEFINE_IO_PACKET_REG(HEADER_WORD1_PAYLOAD_SIZE, 0, 12); - - DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_SLAVE_ADDR, 0, 10); - DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_HS_MASTER_ADDR, 12, 3); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_XFER, 15, USE_REPEAT_START_TOP, CONTINUE); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_REPEAT_START_STOP, 16, STOP_CONDITION, REPEAT_START_CONDITION); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_IE, 17, DISABLE, ENABLE); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_ADDRESS_MODE, 18, SEVEN_BIT, TEN_BIT); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_READ_WRITE, 19, WRITE, READ); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_SEND_START_BYTE, 20, DISABLE, ENABLE); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_ON_NACK, 21, DISABLE, ENABLE); - DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_HS_MODE, 22, DISABLE, ENABLE); - - } - - void I2cBusAccessor::Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm) { - AMS_ASSERT(this->state == State::NotInitialized); - - this->is_power_bus = pb; - this->speed_mode = sm; - this->interrupt_name = intr; - this->registers_phys_addr = reg_paddr; - this->registers_size = reg_size; - this->state = State::Initializing; - } - - void I2cBusAccessor::RegisterDeviceCode(DeviceCode dc) { - AMS_ASSERT(this->state == State::Initializing); - - this->device_code = dc; - } - - void I2cBusAccessor::InitializeDriver() { - AMS_ASSERT(this->state == State::Initializing); - - this->registers = reinterpret_cast(dd::QueryIoMapping(this->registers_phys_addr, this->registers_size)); - AMS_ABORT_UNLESS(this->registers != nullptr); - - this->state = State::Initialized; - } - - void I2cBusAccessor::FinalizeDriver() { - AMS_ASSERT(this->state == State::Initialized); - this->state = State::Initializing; - } - - Result I2cBusAccessor::InitializeDevice(I2cDeviceProperty *device) { - /* Check that the device is valid. */ - AMS_ASSERT(device != nullptr); - AMS_ASSERT(this->state == State::Initialized); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* Increment our user count -- if we're already open, we're done. */ - AMS_ASSERT(this->user_count >= 0); - ++this->user_count; - R_SUCCEED_IF(this->user_count > 1); - - /* Initialize our interrupt event. */ - os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - - /* If we're not power bus, perform power management init. */ - if (!this->is_power_bus) { - /* Initialize regulator library. */ - regulator::Initialize(); - - /* Try to open regulator session. */ - R_TRY(this->TryOpenRegulatorSession()); - - /* If we have a regulator session, set voltage to 2.9V. */ - if (this->has_regulator_session) { - /* NOTE: Nintendo does not check the result, here. */ - regulator::SetVoltageValue(std::addressof(this->regulator_session), 2'900'000u); - } - - /* Initialize clock/reset library. */ - clkrst::Initialize(); - } - - /* Execute initial config. */ - this->ExecuteInitialConfig(); - - /* If we have a regulator session, enable voltage. */ - if (!this->is_power_bus && this->has_regulator_session) { - /* Check whether voltage was already enabled. */ - const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); - - /* NOTE: Nintendo does not check the result of this call. */ - regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); - - /* If we enabled voltage, delay to give our enable time to take. */ - if (!was_enabled) { - os::SleepThread(TimeSpan::FromMicroSeconds(560)); - } - } - - return ResultSuccess(); - } - - void I2cBusAccessor::FinalizeDevice(I2cDeviceProperty *device) { - /* Check that the device is valid. */ - AMS_ASSERT(device != nullptr); - AMS_ASSERT(this->state == State::Initialized); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* Increment our user count -- if we're not the last user, we're done. */ - AMS_ASSERT(this->user_count > 0); - --this->user_count; - if (this->user_count > 0) { - return; - } - - /* Finalize our interrupt event. */ - os::FinalizeInterruptEvent(std::addressof(this->interrupt_event)); - - /* If we have a regulator session, disable voltage. */ - if (this->has_regulator_session) { - /* NOTE: Nintendo does not check the result of this call. */ - regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); - } - - /* Finalize the clock/reset library. */ - clkrst::Finalize(); - - /* If we have a regulator session, close it. */ - if (this->has_regulator_session) { - regulator::CloseSession(std::addressof(this->regulator_session)); - this->has_regulator_session = false; - } - - /* Finalize the regulator library. */ - regulator::Finalize(); - } - - Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) { - /* Check pre-conditions. */ - AMS_ASSERT(device != nullptr); - AMS_ASSERT(src != nullptr); - AMS_ASSERT(src_size > 0); - - if (this->is_power_bus) { - AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); - } else { - AMS_ASSERT(this->state == State::Initialized); - } - - /* Send the data. */ - return this->Send(static_cast(src), src_size, option, device->GetAddress(), device->GetAddressingMode()); - } - - Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) { - /* Check pre-conditions. */ - AMS_ASSERT(device != nullptr); - AMS_ASSERT(dst != nullptr); - AMS_ASSERT(dst_size > 0); - - if (this->is_power_bus) { - AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); - } else { - AMS_ASSERT(this->state == State::Initialized); - } - - /* Send the data. */ - return this->Receive(static_cast(dst), dst_size, option, device->GetAddress(), device->GetAddressingMode()); - } - - void I2cBusAccessor::SuspendBus() { - /* Check that state is valid. */ - AMS_ASSERT(this->state == State::Initialized); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* If we need to, disable clock/voltage appropriately. */ - if (!this->is_power_bus && this->user_count > 0) { - /* Disable clock. */ - { - /* Open a clkrst session. */ - clkrst::ClkRstSession clkrst_session; - R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); - ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; - - /* Set clock disabled for the session. */ - clkrst::SetClockDisabled(std::addressof(clkrst_session)); - } - - /* Disable voltage. */ - if (this->has_regulator_session) { - regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); - } - } - - /* Update state. */ - this->state = State::Suspended; - } - - void I2cBusAccessor::SuspendPowerBus() { - /* Check that state is valid. */ - AMS_ASSERT(this->state == State::Suspended); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* If we need to, disable clock/voltage appropriately. */ - if (this->is_power_bus && this->user_count > 0) { - /* Nothing should actually be done here. */ - } - - /* Update state. */ - this->state = State::PowerBusSuspended; - } - - void I2cBusAccessor::ResumeBus() { - /* Check that state is valid. */ - AMS_ASSERT(this->state == State::Suspended); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* If we need to, enable clock/voltage appropriately. */ - if (!this->is_power_bus && this->user_count > 0) { - /* Enable voltage. */ - if (this->has_regulator_session) { - /* Check whether voltage was already enabled. */ - const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); - - /* NOTE: Nintendo does not check the result of this call. */ - regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); - - /* If we enabled voltage, delay to give our enable time to take. */ - if (!was_enabled) { - os::SleepThread(TimeSpan::FromMicroSeconds(560)); - } - } - - /* Execute initial config, which will enable clock as relevant. */ - this->ExecuteInitialConfig(); - } - - /* Update state. */ - this->state = State::Initialized; - } - - void I2cBusAccessor::ResumePowerBus() { - /* Check that state is valid. */ - AMS_ASSERT(this->state == State::PowerBusSuspended); - - /* Acquire exclusive access. */ - std::scoped_lock lk(this->user_count_mutex); - - /* If we need to, enable clock/voltage appropriately. */ - if (this->is_power_bus && this->user_count > 0) { - /* Execute initial config, which will enable clock as relevant. */ - this->ExecuteInitialConfig(); - } - - /* Update state. */ - this->state = State::Suspended; - } - - Result I2cBusAccessor::TryOpenRegulatorSession() { - /* Ensure we track the session. */ - this->has_regulator_session = true; - auto s_guard = SCOPE_GUARD { this->has_regulator_session = false; }; - - /* Try to open the session. */ - R_TRY_CATCH(regulator::OpenSession(std::addressof(this->regulator_session), this->device_code)) { - R_CATCH(ddsf::ResultDeviceCodeNotFound) { - /* It's okay if the device isn't found, but we don't have a session if so. */ - this->has_regulator_session = false; - } - } R_END_TRY_CATCH; - - /* We opened (or not). */ - s_guard.Cancel(); - return ResultSuccess(); - } - - void I2cBusAccessor::ExecuteInitialConfig() { - /* Lock exclusive access to registers. */ - std::scoped_lock lk(this->register_mutex); - - /* Reset the controller. */ - this->ResetController(); - - /* Set clock registers. */ - this->SetClockRegisters(this->speed_mode); - - /* Set packet mode registers. */ - this->SetPacketModeRegisters(); - - /* Flush fifos. */ - this->FlushFifos(); - } - - Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { - /* Acquire exclusive access to the registers. */ - std::scoped_lock lk(this->register_mutex); - - /* Configure interrupt mask, clear interrupt status. */ - reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); - - reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); - - /* Write the header. */ - this->WriteHeader(Xfer_Write, src_size, option, slave_address, addressing_mode); - - /* Setup tracking variables for the data. */ - const u8 *cur = src; - size_t remaining = src_size; - - while (true) { - /* Get the number of empty bytes in the fifo status. */ - const u32 empty = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_TX_FIFO_EMPTY_CNT)); - - /* Write up to (empty) bytes to the fifo. */ - for (u32 i = 0; remaining > 0 && i < empty; ++i) { - /* Build the data word to send. */ - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - - u32 word = 0; - for (size_t j = 0; j < cur_bytes; ++j) { - word |= cur[j] << (BITSIZEOF(u8) * j); - } - - /* Write the data word. */ - reg::Write(this->registers->tx_packet_fifo, word); - - /* Advance. */ - cur += cur_bytes; - remaining -= cur_bytes; - } - - /* If we're done, break. */ - if (remaining == 0) { - break; - } - - /* Wait for our current data to send. */ - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { - /* We timed out. */ - this->HandleTransactionError(i2c::ResultBusBusy()); - - this->DisableInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimeout(); - } - - /* Check and handle any errors. */ - R_TRY(this->CheckAndHandleError()); - } - - /* Configure interrupt mask to not care about tfifo data req. */ - reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); - - /* Wait for the packet transfer to complete. */ - while (true) { - /* Check and handle any errors. */ - R_TRY(this->CheckAndHandleError()); - - /* Check if packet transfer is done. */ - if (reg::HasValue(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET))) { - break; - } - - /* Wait for our the packet to transfer. */ - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { - /* We timed out. */ - this->HandleTransactionError(i2c::ResultBusBusy()); - - this->DisableInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimeout(); - } - } - - /* Check and handle any errors. */ - R_TRY(this->CheckAndHandleError()); - - /* We're done. */ - this->DisableInterruptMask(); - return ResultSuccess(); - } - - Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { - /* Acquire exclusive access to the registers. */ - std::scoped_lock lk(this->register_mutex); - - /* Configure interrupt mask, clear interrupt status. */ - reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), - I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); - - reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), - I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); - - /* Write the header. */ - this->WriteHeader(Xfer_Read, dst_size, option, slave_address, addressing_mode); - - /* Setup tracking variables for the data. */ - u8 *cur = dst; - size_t remaining = dst_size; - - while (remaining > 0) { - /* Wait for data to come in. */ - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { - /* We timed out. */ - this->HandleTransactionError(i2c::ResultBusBusy()); - - this->DisableInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimeout(); - } - - /* Check and handle any errors. */ - R_TRY(this->CheckAndHandleError()); - - /* Get the number of full bytes in the fifo status. */ - const u32 full = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_RX_FIFO_FULL_CNT)); - - /* Determine how many words we can read. */ - const size_t cur_words = std::min(util::DivideUp(remaining, sizeof(u32)), static_cast(full)); - - /* Read the correct number of words from the fifo. */ - for (size_t i = 0; i < cur_words; ++i) { - /* Read the word from the fifo. */ - const u32 word = reg::Read(this->registers->rx_fifo); - - /* Copy bytes from the word. */ - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - for (size_t j = 0; j < cur_bytes; ++j) { - cur[j] = (word >> (BITSIZEOF(u8) * j)) & 0xFF; - } - - /* Advance. */ - cur += cur_bytes; - remaining -= cur_bytes; - } - } - - /* We're done. */ - return ResultSuccess(); - } - - void I2cBusAccessor::WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { - /* Parse interesting values from our arguments. */ - const bool is_read = xfer == Xfer_Read; - const bool is_7_bit = addressing_mode == AddressingMode_SevenBit; - const bool is_stop = (option & TransactionOption_StopCondition) != 0; - const bool is_hs = this->speed_mode == SpeedMode_HighSpeed; - const u32 slave_addr = ((static_cast(slave_address) & 0x7F) << 1) | (is_read ? 1 : 0); - - /* Flush fifos. */ - this->FlushFifos(); - - /* Enqueue the first header word. */ - reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM (HEADER_WORD0_PROT_HDR_SZ, 1_WORD), - IO_PACKET_BITS_VALUE(HEADER_WORD0_PKT_ID, 0), - IO_PACKET_BITS_VALUE(HEADER_WORD0_CONTROLLER_ID, 0), - IO_PACKET_BITS_ENUM (HEADER_WORD0_PROTOCOL, I2C), - IO_PACKET_BITS_ENUM (HEADER_WORD0_PKT_TYPE, REQUEST)); - - /* Enqueue the second header word. */ - reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_VALUE(HEADER_WORD1_PAYLOAD_SIZE, static_cast(size - 1))); - - /* Enqueue the protocol header word. */ - reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_HS_MODE, is_hs, ENABLE, DISABLE), - IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_ON_NACK, DISABLE), - IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_SEND_START_BYTE, DISABLE), - IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_READ_WRITE, is_read, READ, WRITE), - IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_ADDRESS_MODE, is_7_bit, SEVEN_BIT, TEN_BIT), - IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_IE, ENABLE), - IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_REPEAT_START_STOP, is_stop, STOP_CONDITION, REPEAT_START_CONDITION), - IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_XFER, USE_REPEAT_START_TOP), - IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_HS_MASTER_ADDR, 0), - IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_SLAVE_ADDR, slave_addr)); - } - - void I2cBusAccessor::ResetController() const { - /* Reset the controller. */ - if (!this->is_power_bus) { - /* Open a clkrst session. */ - clkrst::ClkRstSession clkrst_session; - R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); - ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; - - /* Reset the controller, setting clock rate to 408 MHz / 5 (to account for clock divisor). */ - /* NOTE: Nintendo does not check result for any of these calls. */ - clkrst::SetResetAsserted(std::addressof(clkrst_session)); - clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (4 + 1)); - clkrst::SetResetDeasserted(std::addressof(clkrst_session)); - } - } - - void I2cBusAccessor::ClearBus() const { - /* Try to clear the bus up to three times. */ - constexpr int MaxRetryCount = 3; - constexpr int BusyLoopMicroSeconds = 1000; - - int try_count = 0; - bool need_retry; - do { - /* Update trackers. */ - ++try_count; - need_retry = false; - - /* Reset the controller. */ - this->ResetController(); - - /* Configure the sclk threshold for bus clear config. */ - reg::Write(this->registers->bus_clear_config, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9)); - - /* Set stop cond and terminate in bus clear config. */ - reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, STOP)); - reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE)); - - /* Set master config load, busy loop up to 1ms for it to take. */ - reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); - - const os::Tick start_tick_a = os::GetSystemTick(); - while (reg::HasValue(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE))) { - if ((os::GetSystemTick() - start_tick_a).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { - need_retry = true; - break; - } - } - - if (need_retry) { - continue; - } - - /* Set bus clear enable, wait up to 1ms for it to take. */ - reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); - - const os::Tick start_tick_b = os::GetSystemTick(); - while (reg::HasValue(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE))) { - if ((os::GetSystemTick() - start_tick_b).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { - need_retry = true; - break; - } - } - - if (need_retry) { - continue; - } - - /* Wait up to 1ms for the bus clear to complete. */ - const os::Tick start_tick_c = os::GetSystemTick(); - while (reg::HasValue(this->registers->bus_clear_status, I2C_REG_BITS_ENUM(BUS_CLEAR_STATUS_BC_STATUS, NOT_CLEARED))) { - if ((os::GetSystemTick() - start_tick_c).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { - need_retry = true; - break; - } - } - - if (need_retry) { - continue; - } - } while (try_count < MaxRetryCount && need_retry); - } - - void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) { - /* Determine parameters for the speed mode. */ - u32 t_high, t_low, clk_div, debounce, src_div; - bool high_speed = false; - - if (this->is_power_bus) { - t_high = 0x02; - t_low = 0x04; - clk_div = 0x05; - debounce = 0x02; - src_div = 0; /* unused */ - } else { - switch (speed_mode) { - case SpeedMode_Standard: - t_high = 0x02; - t_low = 0x04; - clk_div = 0x19; - debounce = 0x02; - src_div = 0x13; - break; - case SpeedMode_Fast: - t_high = 0x02; - t_low = 0x04; - clk_div = 0x19; - debounce = 0x02; - src_div = 0x04; - break; - case SpeedMode_FastPlus: - t_high = 0x02; - t_low = 0x04; - clk_div = 0x10; - debounce = 0x00; - src_div = 0x02; - break; - case SpeedMode_HighSpeed: - t_high = 0x03; - t_low = 0x08; - clk_div = 0x02; - debounce = 0x00; - src_div = 0x02; - high_speed = true; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - /* Write the clock divisors. */ - if (high_speed) { - reg::Write(this->registers->hs_interface_timing_0, I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_THIGH, t_high), - I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_TLOW, t_low)); - - reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, clk_div)); - } else { - reg::Write(this->registers->interface_timing_0, I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_THIGH, t_high), - I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_TLOW, t_low)); - - reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, clk_div)); - } - - /* Configure debounce. */ - reg::Write(this->registers->cnfg, I2C_REG_BITS_VALUE(I2C_CNFG_DEBOUNCE_CNT, debounce)); - reg::Read(this->registers->cnfg); - - /* Set the clock rate, if we should. */ - if (!this->is_power_bus) { - /* Open a clkrst session. */ - clkrst::ClkRstSession clkrst_session; - R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); - ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; - - /* Reset the controller, setting clock rate to 408 MHz / (src_div + 1). */ - /* NOTE: Nintendo does not check result for any of these calls. */ - clkrst::SetResetAsserted(std::addressof(clkrst_session)); - clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (src_div + 1)); - clkrst::SetResetDeasserted(std::addressof(clkrst_session)); - } - } - - void I2cBusAccessor::SetPacketModeRegisters() { - /* Set packet mode enable. */ - reg::ReadWrite(this->registers->cnfg, I2C_REG_BITS_ENUM(I2C_CNFG_PACKET_MODE_EN, GO)); - - /* Set master config load. */ - reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); - - /* Set tx/fifo triggers to default (maximum values). */ - reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), - I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7)); - } - - Result I2cBusAccessor::FlushFifos() { - /* Flush the fifo. */ - reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), - I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7), - I2C_REG_BITS_ENUM (FIFO_CONTROL_RX_FIFO_FLUSH, SET), - I2C_REG_BITS_ENUM (FIFO_CONTROL_TX_FIFO_FLUSH, SET)); - - /* Wait up to 5 ms for the flush to complete. */ - int count = 0; - while (!reg::HasValue(this->registers->fifo_control, I2C_REG_BITS_ENUM(FIFO_CONTROL_FIFO_FLUSH, RX_UNSET_TX_UNSET))) { - R_UNLESS((++count < 5), i2c::ResultBusBusy()); - os::SleepThread(TimeSpan::FromMilliSeconds(1)); - } - - return ResultSuccess(); - } - - Result I2cBusAccessor::GetTransactionResult() const { - /* Get packet status/interrupt status. */ - volatile u32 packet_status = reg::Read(this->registers->packet_transfer_status); - volatile u32 interrupt_status = reg::Read(this->registers->interrupt_status_register); - - /* Check for ack. */ - R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, UNSET)), i2c::ResultNoAck()); - R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, UNSET)), i2c::ResultNoAck()); - R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, UNSET)), i2c::ResultNoAck()); - - /* If we lost arbitration, we'll need to clear the bus. */ - auto clear_guard = SCOPE_GUARD { this->ClearBus(); }; - - /* Check for arb lost. */ - R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, UNSET)), i2c::ResultBusBusy()); - R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, UNSET)), i2c::ResultBusBusy()); - - clear_guard.Cancel(); - return ResultSuccess(); - } - - void I2cBusAccessor::HandleTransactionError(Result result) { - R_TRY_CATCH(result) { - R_CATCH(i2c::ResultNoAck, i2c::ResultBusBusy) { - /* Reset the controller. */ - this->ResetController(); - - /* Set clock registers. */ - this->SetClockRegisters(this->speed_mode); - - /* Set packet mode registers. */ - this->SetPacketModeRegisters(); - - /* Flush fifos. */ - this->FlushFifos(); - } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp deleted file mode 100644 index 8691dba..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "i2c_i2c_registers.hpp" - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - class I2cBusAccessor : public ::ams::i2c::driver::II2cDriver { - NON_COPYABLE(I2cBusAccessor); - NON_MOVEABLE(I2cBusAccessor); - AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::board::nintendo_nx::impl::I2cBusAccessor, ::ams::i2c::driver::II2cDriver); - private: - enum class State { - NotInitialized = 0, - Initializing = 1, - Initialized = 2, - Suspended = 3, - PowerBusSuspended = 4, - }; - - enum Xfer { - Xfer_Write = 0, - Xfer_Read = 1, - }; - private: - volatile I2cRegisters *registers; - SpeedMode speed_mode; - os::InterruptEventType interrupt_event; - int user_count; - os::SdkMutex user_count_mutex; - os::SdkMutex register_mutex; - regulator::RegulatorSession regulator_session; - bool has_regulator_session; - State state; - os::SdkMutex transaction_order_mutex; - bool is_power_bus; - dd::PhysicalAddress registers_phys_addr; - size_t registers_size; - os::InterruptName interrupt_name; - DeviceCode device_code; - util::IntrusiveListNode bus_accessor_list_node; - public: - using BusAccessorListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&I2cBusAccessor::bus_accessor_list_node>; - using BusAccessorList = typename BusAccessorListTraits::ListType; - friend class util::IntrusiveList>; - public: - I2cBusAccessor() - : registers(nullptr), speed_mode(SpeedMode_Fast), user_count(0), user_count_mutex(), - register_mutex(), has_regulator_session(false), state(State::NotInitialized), transaction_order_mutex(), - is_power_bus(false), registers_phys_addr(0), registers_size(0), interrupt_name(), device_code(-1), bus_accessor_list_node() - { - /* ... */ - } - - void Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm); - void RegisterDeviceCode(DeviceCode device_code); - - SpeedMode GetSpeedMode() const { return this->speed_mode; } - dd::PhysicalAddress GetRegistersPhysicalAddress() const { return this->registers_phys_addr; } - size_t GetRegistersSize() const { return this->registers_size; } - os::InterruptName GetInterruptName() const { return this->interrupt_name; } - private: - Result TryOpenRegulatorSession(); - - void ExecuteInitialConfig(); - - Result Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); - Result Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); - - void WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); - - void ResetController() const; - void ClearBus() const; - void SetClockRegisters(SpeedMode speed_mode); - void SetPacketModeRegisters(); - - Result FlushFifos(); - - Result GetTransactionResult() const; - void HandleTransactionError(Result result); - - void DisableInterruptMask() { - reg::Write(this->registers->interrupt_mask_register, 0); - reg::Read(this->registers->interrupt_mask_register); - } - - Result CheckAndHandleError() { - const Result result = this->GetTransactionResult(); - this->HandleTransactionError(result); - if (R_FAILED(result)) { - this->DisableInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return result; - } - - return ResultSuccess(); - } - public: - virtual void InitializeDriver() override; - virtual void FinalizeDriver() override; - - virtual Result InitializeDevice(I2cDeviceProperty *device) override; - virtual void FinalizeDevice(I2cDeviceProperty *device) override; - - virtual Result Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) override; - virtual Result Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) override; - - virtual os::SdkMutex &GetTransactionOrderMutex() override { - return this->transaction_order_mutex; - } - - virtual void SuspendBus() override; - virtual void SuspendPowerBus() override; - - virtual void ResumeBus() override; - virtual void ResumePowerBus() override; - - virtual const DeviceCode &GetDeviceCode() const override { - return this->device_code; - } - }; - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp deleted file mode 100644 index 64ae466..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "i2c_bus_accessor.hpp" -#include "i2c_i_allocator.hpp" - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - class I2cBusAccessorManager : public IAllocator { - /* ... */ - }; - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp deleted file mode 100644 index 726d876..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "i2c_i_allocator.hpp" - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - class I2cDevicePropertyManager : public IAllocator { - /* ... */ - }; - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp deleted file mode 100644 index c8925ce..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - struct I2cRegisters { - volatile u32 cnfg; - volatile u32 cmd_addr0; - volatile u32 cmd_addr1; - volatile u32 cmd_data1; - volatile u32 cmd_data2; - volatile u32 _14; - volatile u32 _18; - volatile u32 status; - volatile u32 sl_cnfg; - volatile u32 sl_rcvd; - volatile u32 sl_status; - volatile u32 sl_addr1; - volatile u32 sl_addr2; - volatile u32 tlow_sext; - volatile u32 _38; - volatile u32 sl_delay_count; - volatile u32 sl_int_mask; - volatile u32 sl_int_source; - volatile u32 sl_int_set; - volatile u32 _4c; - volatile u32 tx_packet_fifo; - volatile u32 rx_fifo; - volatile u32 packet_transfer_status; - volatile u32 fifo_control; - volatile u32 fifo_status; - volatile u32 interrupt_mask_register; - volatile u32 interrupt_status_register; - volatile u32 clk_divisor_register; - volatile u32 interrupt_source_register; - volatile u32 interrupt_set_register; - volatile u32 slv_tx_packet_fifo; - volatile u32 slv_rx_fifo; - volatile u32 slv_packet_status; - volatile u32 bus_clear_config; - volatile u32 bus_clear_status; - volatile u32 config_load; - volatile u32 _90; - volatile u32 interface_timing_0; - volatile u32 interface_timing_1; - volatile u32 hs_interface_timing_0; - volatile u32 hs_interface_timing_1; - }; - static_assert(sizeof(I2cRegisters) == 0xA4); - -} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp deleted file mode 100644 index 270c882..0000000 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::i2c::driver::board::nintendo_nx::impl { - - template - class IAllocator { - private: - using T = typename ListType::value_type; - private: - ams::MemoryResource *memory_resource; - ListType list; - mutable os::SdkMutex list_lock; - public: - IAllocator(ams::MemoryResource *mr) : memory_resource(mr), list(), list_lock() { /* ... */ } - - ~IAllocator() { - std::scoped_lock lk(this->list_lock); - - /* Remove all entries. */ - auto it = this->list.begin(); - while (it != this->list.end()) { - T *obj = std::addressof(*it); - it = this->list.erase(it); - - obj->~T(); - this->memory_resource->Deallocate(obj, sizeof(T)); - } - } - - template - T *Allocate(Args &&...args) { - std::scoped_lock lk(this->list_lock); - - /* Allocate space for the object. */ - void *storage = this->memory_resource->Allocate(sizeof(T), alignof(T)); - AMS_ABORT_UNLESS(storage != nullptr); - - /* Construct the object. */ - T *t = new (static_cast(storage)) T(std::forward(args)...); - - /* Link the object into our list. */ - this->list.push_back(*t); - - return t; - } - - template - T *Find(F f) { - std::scoped_lock lk(this->list_lock); - - for (T &it : this->list) { - if (f(static_cast(it))) { - return std::addressof(it); - } - } - - return nullptr; - } - - /* TODO: Support free */ - }; - -} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.cpp new file mode 100644 index 0000000..fc35ce9 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.cpp @@ -0,0 +1,138 @@ +/* + * 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 "pinmux_pad_index.hpp" +#include "pinmux_board_driver_api.hpp" +#include "pinmux_platform_pads.hpp" + +namespace ams::pinmux::driver::board::nintendo_nx { + + namespace { + + constinit bool g_initialized = false; + + #include "pinmux_initial_pad_config_icosa.inc" + #include "pinmux_initial_pad_config_hoag.inc" + #include "pinmux_initial_pad_config_iowa.inc" + #include "pinmux_initial_pad_config_calcio.inc" + #include "pinmux_initial_pad_config_aula.inc" + + #include "pinmux_initial_drive_pad_config.inc" + #include "pinmux_initial_drive_pad_config_hoag.inc" + + } + + bool IsInitialized() { + return g_initialized; + } + + void Initialize() { + InitializePlatformPads(); + g_initialized = true; + } + + void Finalize() { + /* ... */ + } + + void SetInitialConfig() { + const PinmuxPadConfig *configs = nullptr; + size_t num_configs = 0; + bool is_mariko = false; + switch (spl::GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = PinmuxPadConfigsIcosa; + num_configs = NumPinmuxPadConfigsIcosa; + is_mariko = false; + break; + case spl::HardwareType::Hoag: + configs = PinmuxPadConfigsHoag; + num_configs = NumPinmuxPadConfigsHoag; + is_mariko = true; + break; + case spl::HardwareType::Iowa: + configs = PinmuxPadConfigsIowa; + num_configs = NumPinmuxPadConfigsIowa; + is_mariko = true; + break; + case spl::HardwareType::Calcio: + configs = PinmuxPadConfigsCalcio; + num_configs = NumPinmuxPadConfigsCalcio; + is_mariko = true; + break; + case spl::HardwareType::Aula: + configs = PinmuxPadConfigsAula; + num_configs = NumPinmuxPadConfigsAula; + is_mariko = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_ABORT_UNLESS(configs != nullptr); + + for (size_t i = 0; i < num_configs; ++i) { + UpdateSinglePinmuxPad(configs[i]); + } + + if (is_mariko) { + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Clk, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Cmd, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat0, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat1, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat2, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat3, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat4, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat5, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat6, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat7, 0x2000, 0x2000 }); + } + } + + void SetInitialDrivePadConfig() { + const PinmuxDrivePadConfig *configs = nullptr; + size_t num_configs = 0; + switch (spl::GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::Hoag: + configs = PinmuxDrivePadConfigsHoag; + num_configs = NumPinmuxDrivePadConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::Calcio: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::Aula: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_ABORT_UNLESS(configs != nullptr); + + for (size_t i = 0; i < num_configs; ++i) { + UpdateSinglePinmuxDrivePad(configs[i]); + } + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.hpp new file mode 100644 index 0000000..48be15d --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_board_driver_api.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::pinmux::driver::board::nintendo_nx { + + bool IsInitialized(); + + void Initialize(); + void Finalize(); + + void SetInitialConfig(); + void SetInitialDrivePadConfig(); + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_drive_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_drive_pad_characters.inc new file mode 100644 index 0000000..a6ed61f --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_drive_pad_characters.inc @@ -0,0 +1,170 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadCharacter PinmuxDrivePadCharacters[] = { + { 0x08E4, 0x01F1F000, "AlsProxInt" }, + { 0x08E8, 0x01F1F000, "ApReady" }, + { 0x08EC, 0x01F1F000, "ApWakeBt" }, + { 0x08F0, 0x01F1F000, "ApWakeNfc" }, + { 0x08F4, 0x01F1F000, "AudMclk" }, + { 0x08F8, 0x01F1F000, "BattBcl" }, + { 0x08FC, 0x01F1F000, "BtRst" }, + { 0x0900, 0x01F1F000, "BtWakeAp" }, + { 0x0904, 0x01F1F000, "ButtonHome" }, + { 0x0908, 0x01F1F000, "ButtonPowerOn" }, + { 0x090C, 0x01F1F000, "ButtonSlideSw" }, + { 0x0910, 0x01F1F000, "ButtonVolDown" }, + { 0x0914, 0x01F1F000, "ButtonVolUp" }, + { 0x0918, 0x01F1F000, "Cam1Mclk" }, + { 0x091C, 0x01F1F000, "Cam1Pwdn" }, + { 0x0920, 0x01F1F000, "Cam1Strobe" }, + { 0x0924, 0x01F1F000, "Cam2Mclk" }, + { 0x0928, 0x01F1F000, "Cam2Pwdn" }, + { 0x092C, 0x01F1F000, "CamAfEn" }, + { 0x0930, 0x01F1F000, "CamFlashEn" }, + { 0x0934, 0x01F1F000, "CamI2cScl" }, + { 0x0938, 0x01F1F000, "CamI2cSda" }, + { 0x093C, 0x01F1F000, "CamRst" }, + { 0x0940, 0x01F1F000, "Clk32kIn" }, + { 0x0944, 0x01F1F000, "Clk32kOut" }, + { 0x0948, 0x01F1F000, "ClkReq" }, + { 0x094C, 0x01F1F000, "CorePwrReq" }, + { 0x0950, 0x01F1F000, "CpuPwrReq" }, + { 0x0954, 0xF0000000, "Dap1Din" }, + { 0x0958, 0xF0000000, "Dap1Dout" }, + { 0x095C, 0xF0000000, "Dap1Fs" }, + { 0x0960, 0xF0000000, "Dap1Sclk" }, + { 0x0964, 0xF0000000, "Dap2Din" }, + { 0x0968, 0xF0000000, "Dap2Dout" }, + { 0x096C, 0xF0000000, "Dap2Fs" }, + { 0x0970, 0xF0000000, "Dap2Sclk" }, + { 0x0974, 0x01F1F000, "Dap4Din" }, + { 0x0978, 0x01F1F000, "Dap4Dout" }, + { 0x097C, 0x01F1F000, "Dap4Fs" }, + { 0x0980, 0x01F1F000, "Dap4Sclk" }, + { 0x0984, 0x01F1F000, "Dmic1Clk" }, + { 0x0988, 0x01F1F000, "Dmic1Dat" }, + { 0x098C, 0x01F1F000, "Dmic2Clk" }, + { 0x0990, 0x01F1F000, "Dmic2Dat" }, + { 0x0994, 0x01F1F000, "Dmic3Clk" }, + { 0x0998, 0x01F1F000, "Dmic3Dat" }, + { 0x099C, 0x01F1F000, "DpHpd" }, + { 0x09A0, 0x01F1F000, "DvfsClk" }, + { 0x09A4, 0x01F1F000, "DvfsPwm" }, + { 0x09A8, 0x01F1F000, "Gen1I2cScl" }, + { 0x09AC, 0x01F1F000, "Gen1I2cSda" }, + { 0x09B0, 0x01F1F000, "Gen2I2cScl" }, + { 0x09B4, 0x01F1F000, "Gen2I2cSda" }, + { 0x09B8, 0x01F1F000, "Gen3I2cScl" }, + { 0x09BC, 0x01F1F000, "Gen3I2cSda" }, + { 0x09C0, 0x01F1F000, "GpioPa6" }, + { 0x09C4, 0x01F1F000, "GpioPcc7" }, + { 0x09C8, 0x01F1F000, "GpioPe6" }, + { 0x09CC, 0x01F1F000, "GpioPe7" }, + { 0x09D0, 0x01F1F000, "GpioPh6" }, + { 0x09D4, 0xF0000000, "GpioPk0" }, + { 0x09D8, 0xF0000000, "GpioPk1" }, + { 0x09DC, 0xF0000000, "GpioPk2" }, + { 0x09E0, 0xF0000000, "GpioPk3" }, + { 0x09E4, 0xF0000000, "GpioPk4" }, + { 0x09E8, 0xF0000000, "GpioPk5" }, + { 0x09EC, 0xF0000000, "GpioPk6" }, + { 0x09F0, 0xF0000000, "GpioPk7" }, + { 0x09F4, 0xF0000000, "GpioPl0" }, + { 0x09F8, 0xF0000000, "GpioPl1" }, + { 0x09FC, 0x07F7F000, "GpioPz0" }, + { 0x0A00, 0x07F7F000, "GpioPz1" }, + { 0x0A04, 0x07F7F000, "GpioPz2" }, + { 0x0A08, 0x07F7F000, "GpioPz3" }, + { 0x0A0C, 0x07F7F000, "GpioPz4" }, + { 0x0A10, 0x07F7F000, "GpioPz5" }, + { 0x0A14, 0x01F1F000, "GpioX1Aud" }, + { 0x0A18, 0x01F1F000, "GpioX3Aud" }, + { 0x0A1C, 0x01F1F000, "GpsEn" }, + { 0x0A20, 0x01F1F000, "GpsRst" }, + { 0x0A24, 0x01F1F000, "HdmiCec" }, + { 0x0A28, 0x01F1F000, "HdmiIntDpHpd" }, + { 0x0A2C, 0x01F1F000, "JtagRtck" }, + { 0x0A30, 0x01F1F000, "LcdBlEn" }, + { 0x0A34, 0x01F1F000, "LcdBlPwm" }, + { 0x0A38, 0x01F1F000, "LcdGpio1" }, + { 0x0A3C, 0x01F1F000, "LcdGpio2" }, + { 0x0A40, 0x01F1F000, "LcdRst" }, + { 0x0A44, 0x01F1F000, "LcdTe" }, + { 0x0A48, 0x01F1F000, "ModemWakeAp" }, + { 0x0A4C, 0x01F1F000, "MotionInt" }, + { 0x0A50, 0x01F1F000, "NfcEn" }, + { 0x0A54, 0x01F1F000, "NfcInt" }, + { 0x0A58, 0x01F1F000, "PexL0ClkReqN" }, + { 0x0A5C, 0x01F1F000, "PexL0RstN" }, + { 0x0A60, 0x01F1F000, "PexL1ClkreqN" }, + { 0x0A64, 0x01F1F000, "PexL1RstN" }, + { 0x0A68, 0x01F1F000, "PexWakeN" }, + { 0x0A6C, 0x01F1F000, "PwrI2cScl" }, + { 0x0A70, 0x01F1F000, "PwrI2cSda" }, + { 0x0A74, 0x01F1F000, "PwrIntN" }, + { 0x0A78, 0x07F7F000, "QspiComp" }, + { 0x0A90, 0xF0000000, "QspiSck" }, + { 0x0A94, 0x01F1F000, "SataLedActive" }, + { 0x0A98, 0xF7F7F000, "Sdmmc1Pad" }, + { 0x0AB0, 0xF7F7F000, "Sdmmc3Pad" }, + { 0x0AC8, 0x01F1F000, "Shutdown" }, + { 0x0ACC, 0x01F1F000, "SpdifIn" }, + { 0x0AD0, 0x01F1F000, "SpdifOut" }, + { 0x0AD4, 0xF0000000, "Spi1Cs0" }, + { 0x0AD8, 0xF0000000, "Spi1Cs1" }, + { 0x0ADC, 0xF0000000, "Spi1Miso" }, + { 0x0AE0, 0xF0000000, "Spi1Mosi" }, + { 0x0AE4, 0xF0000000, "Spi1Sck" }, + { 0x0AE8, 0xF0000000, "Spi2Cs0" }, + { 0x0AEC, 0xF0000000, "Spi2Cs1" }, + { 0x0AF0, 0xF0000000, "Spi2Miso" }, + { 0x0AF4, 0xF0000000, "Spi2Mosi" }, + { 0x0AF8, 0xF0000000, "Spi2Sck" }, + { 0x0AFC, 0xF0000000, "Spi4Cs0" }, + { 0x0B00, 0xF0000000, "Spi4Miso" }, + { 0x0B04, 0xF0000000, "Spi4Mosi" }, + { 0x0B08, 0xF0000000, "Spi4Sck" }, + { 0x0B0C, 0x01F1F000, "TempAlert" }, + { 0x0B10, 0x01F1F000, "TouchClk" }, + { 0x0B14, 0x01F1F000, "TouchInt" }, + { 0x0B18, 0x01F1F000, "TouchRst" }, + { 0x0B1C, 0x01F1F000, "Uart1Cts" }, + { 0x0B20, 0x01F1F000, "Uart1Rts" }, + { 0x0B24, 0x01F1F000, "Uart1Rx" }, + { 0x0B28, 0x01F1F000, "Uart1Tx" }, + { 0x0B2C, 0x01F1F000, "Uart2Cts" }, + { 0x0B30, 0x01F1F000, "Uart2Rts" }, + { 0x0B34, 0x01F1F000, "Uart2Rx" }, + { 0x0B38, 0x01F1F000, "Uart2Tx" }, + { 0x0B3C, 0x01F1F000, "Uart3Cts" }, + { 0x0B40, 0x01F1F000, "Uart3Rts" }, + { 0x0B44, 0x01F1F000, "Uart3Rx" }, + { 0x0B48, 0x01F1F000, "Uart3Tx" }, + { 0x0B4C, 0x01F1F000, "Uart4Cts" }, + { 0x0B50, 0x01F1F000, "Uart4Rts" }, + { 0x0B54, 0x01F1F000, "Uart4Rx" }, + { 0x0B58, 0x01F1F000, "Uart4Tx" }, + { 0x0B5C, 0x01F1F000, "UsbVbusEn0" }, + { 0x0B60, 0x01F1F000, "UsbVbusEn1" }, + { 0x0B64, 0x01F1F000, "WifiEn" }, + { 0x0B68, 0x01F1F000, "WifiRst" }, + { 0x0B6C, 0x01F1F000, "WifiWakeAp" }, +}; + +constexpr inline size_t NumPinmuxDrivePadCharacters = util::size(PinmuxDrivePadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config.inc new file mode 100644 index 0000000..2caa615 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config.inc @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigs[] = { + { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, +}; + +constexpr inline const size_t NumPinmuxDrivePadConfigs = util::size(PinmuxDrivePadConfigs); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config_hoag.inc new file mode 100644 index 0000000..8831b8f --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_drive_pad_config_hoag.inc @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigsHoag[] = { + { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cScl, 0x00004000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cSda, 0x00004000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, +}; + +constexpr inline const size_t NumPinmuxDrivePadConfigsHoag = util::size(PinmuxDrivePadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_aula.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_aula.inc new file mode 100644 index 0000000..14bcc76 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_aula.inc @@ -0,0 +1,196 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsAula[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsAula = util::size(PinmuxPadConfigsAula); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_calcio.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_calcio.inc new file mode 100644 index 0000000..94bca5c --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_calcio.inc @@ -0,0 +1,196 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsCalcio[] = { +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiCec, 0x00000240, 0x0000027F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000204, 0x0000027F }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Spi1Mosi, 0x0000000C, 0x0000007F }, +{ PinmuxPadIndex_Spi1Miso, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Spi1Sck, 0x0000000C, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_DpHpd0, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000024, 0x0000027F }, +{ PinmuxPadIndex_AudMclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioX3Aud, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cSda, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamFlashEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SataLedActive, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_MotionInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz4, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000018 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000018 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsCalcio = util::size(PinmuxPadConfigsCalcio); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_hoag.inc new file mode 100644 index 0000000..d0efa99 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_hoag.inc @@ -0,0 +1,196 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsHoag[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_TouchClk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart3Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPe7, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Fs, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk7, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000204, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsHoag = util::size(PinmuxPadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_icosa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_icosa.inc new file mode 100644 index 0000000..e1cee6f --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_icosa.inc @@ -0,0 +1,183 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsIcosa[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x00000267 }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x00000267 }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000048, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000060 }, +{ PinmuxPadIndex_PwrIntN, 0x00000030, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_QspiIo0, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_QspiIo1, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_QspiSck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_QspiCsN, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk7, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsIcosa = util::size(PinmuxPadConfigsIcosa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_iowa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_iowa.inc new file mode 100644 index 0000000..3b0022e --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_initial_pad_config_iowa.inc @@ -0,0 +1,196 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsIowa[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsIowa = util::size(PinmuxPadConfigsIowa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_characters.inc new file mode 100644 index 0000000..a84081d --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_characters.inc @@ -0,0 +1,197 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +constexpr inline const PinmuxPadCharacter PinmuxPadCharacters[] = { + { 0x3000, 0x072FF, 0x01, "Sdmmc1Clk" }, + { 0x3004, 0x072FF, 0x02, "Sdmmc1Cmd" }, + { 0x3008, 0x072FF, 0x02, "Sdmmc1Dat3" }, + { 0x300C, 0x072FF, 0x02, "Sdmmc1Dat2" }, + { 0x3010, 0x072FF, 0x02, "Sdmmc1Dat1" }, + { 0x3014, 0x072FF, 0x01, "Sdmmc1Dat0" }, + { 0x301C, 0x072FF, 0x01, "Sdmmc3Clk" }, + { 0x3020, 0x072FF, 0x01, "Sdmmc3Cmd" }, + { 0x3024, 0x072FF, 0x01, "Sdmmc3Dat0" }, + { 0x3028, 0x072FF, 0x01, "Sdmmc3Dat1" }, + { 0x302C, 0x072FF, 0x01, "Sdmmc3Dat2" }, + { 0x3030, 0x072FF, 0x01, "Sdmmc3Dat3" }, + { 0x3038, 0x01DFF, 0x01, "PexL0RstN" }, + { 0x303C, 0x01DFF, 0x01, "PexL0ClkreqN" }, + { 0x3040, 0x01DFF, 0x01, "PexWakeN" }, + { 0x3044, 0x01DFF, 0x01, "PexL1RstN" }, + { 0x3048, 0x01DFF, 0x01, "PexL1ClkreqN" }, + { 0x304C, 0x019FF, 0x01, "SataLedActive" }, + { 0x3050, 0x1F2FF, 0x01, "Spi1Mosi" }, + { 0x3054, 0x1F2FF, 0x01, "Spi1Miso" }, + { 0x3058, 0x1F2FF, 0x01, "Spi1Sck" }, + { 0x305C, 0x1F2FF, 0x01, "Spi1Cs0" }, + { 0x3060, 0x1F2FF, 0x01, "Spi1Cs1" }, + { 0x3064, 0x072FF, 0x02, "Spi2Mosi" }, + { 0x3068, 0x072FF, 0x02, "Spi2Miso" }, + { 0x306C, 0x072FF, 0x02, "Spi2Sck" }, + { 0x3070, 0x072FF, 0x02, "Spi2Cs0" }, + { 0x3074, 0x072FF, 0x01, "Spi2Cs1" }, + { 0x3078, 0x1F2FF, 0x01, "Spi4Mosi" }, + { 0x307C, 0x1F2FF, 0x01, "Spi4Miso" }, + { 0x3080, 0x1F2FF, 0x01, "Spi4Sck" }, + { 0x3084, 0x1F2FF, 0x01, "Spi4Cs0" }, + { 0x3088, 0x072FF, 0x01, "QspiSck" }, + { 0x308C, 0x072FF, 0x01, "QspiCsN" }, + { 0x3090, 0x072FF, 0x01, "QspiIo0" }, + { 0x3094, 0x072FF, 0x01, "QspiIo1" }, + { 0x3098, 0x072FF, 0x01, "QspiIo2" }, + { 0x309C, 0x072FF, 0x01, "QspiIo3" }, + { 0x30A4, 0x019FF, 0x02, "Dmic1Clk" }, + { 0x30A8, 0x019FF, 0x02, "Dmic1Dat" }, + { 0x30AC, 0x019FF, 0x02, "Dmic2Clk" }, + { 0x30B0, 0x019FF, 0x02, "Dmic2Dat" }, + { 0x30B4, 0x019FF, 0x02, "Dmic3Clk" }, + { 0x30B8, 0x019FF, 0x02, "Dmic3Dat" }, + { 0x30BC, 0x01DFF, 0x01, "Gen1I2cScl" }, + { 0x30C0, 0x01DFF, 0x01, "Gen1I2cSda" }, + { 0x30C4, 0x01DFF, 0x01, "Gen2I2cScl" }, + { 0x30C8, 0x01DFF, 0x01, "Gen2I2cSda" }, + { 0x30CC, 0x01DFF, 0x01, "Gen3I2cScl" }, + { 0x30D0, 0x01DFF, 0x01, "Gen3I2cSda" }, + { 0x30D4, 0x01DFF, 0x02, "CamI2cScl" }, + { 0x30D8, 0x01DFF, 0x02, "CamI2cSda" }, + { 0x30DC, 0x01DFF, 0x01, "PwrI2cScl" }, + { 0x30E0, 0x01DFF, 0x01, "PwrI2cSda" }, + { 0x30E4, 0x019FF, 0x01, "Uart1Tx" }, + { 0x30E8, 0x019FF, 0x01, "Uart1Rx" }, + { 0x30EC, 0x019FF, 0x01, "Uart1Rts" }, + { 0x30F0, 0x019FF, 0x01, "Uart1Cts" }, + { 0x30F4, 0x019FF, 0x00, "Uart2Tx" }, + { 0x30F8, 0x019FF, 0x00, "Uart2Rx" }, + { 0x30FC, 0x019FF, 0x02, "Uart2Rts" }, + { 0x3100, 0x019FF, 0x02, "Uart2Cts" }, + { 0x3104, 0x019FF, 0x02, "Uart3Tx" }, + { 0x3108, 0x019FF, 0x02, "Uart3Rx" }, + { 0x310C, 0x019FF, 0x02, "Uart3Rts" }, + { 0x3110, 0x019FF, 0x02, "Uart3Cts" }, + { 0x3114, 0x019FF, 0x02, "Uart4Tx" }, + { 0x3118, 0x019FF, 0x02, "Uart4Rx" }, + { 0x311C, 0x019FF, 0x02, "Uart4Rts" }, + { 0x3120, 0x019FF, 0x02, "Uart4Cts" }, + { 0x3124, 0x072FF, 0x01, "Dap1Fs" }, + { 0x3128, 0x072FF, 0x01, "Dap1Din" }, + { 0x312C, 0x072FF, 0x01, "Dap1Dout" }, + { 0x3130, 0x072FF, 0x01, "Dap1Sclk" }, + { 0x3134, 0x072FF, 0x01, "Dap2Fs" }, + { 0x3138, 0x072FF, 0x01, "Dap2Din" }, + { 0x313C, 0x072FF, 0x01, "Dap2Dout" }, + { 0x3140, 0x072FF, 0x01, "Dap2Sclk" }, + { 0x3144, 0x072FF, 0x01, "Dap4Fs" }, + { 0x3148, 0x072FF, 0x01, "Dap4Din" }, + { 0x314C, 0x072FF, 0x01, "Dap4Dout" }, + { 0x3150, 0x072FF, 0x01, "Dap4Sclk" }, + { 0x3154, 0x072FF, 0x01, "Cam1Mclk" }, + { 0x3158, 0x072FF, 0x01, "Cam2Mclk" }, + { 0x315C, 0x072FF, 0x01, "JtagRtck" }, + { 0x3160, 0x0118C, 0xFF, "Clk32kIn" }, + { 0x3164, 0x072FF, 0x02, "Clk32kOut" }, + { 0x3168, 0x01DFF, 0x01, "BattBcl" }, + { 0x316C, 0x011CC, 0xFF, "ClkReq" }, + { 0x3170, 0x011CC, 0xFF, "CpuPwrReq" }, + { 0x3174, 0x011CC, 0xFF, "PwrIntN" }, + { 0x3178, 0x011CC, 0xFF, "Shutdown" }, + { 0x317C, 0x011CC, 0xFF, "CorePwrReq" }, + { 0x3180, 0x019FF, 0x01, "AudMclk" }, + { 0x3184, 0x019FF, 0x00, "DvfsPwm" }, + { 0x3188, 0x019FF, 0x00, "DvfsClk" }, + { 0x318C, 0x019FF, 0x00, "GpioX1Aud" }, + { 0x3190, 0x019FF, 0x00, "GpioX3Aud" }, + { 0x3194, 0x01DFF, 0x00, "GpioPcc7" }, + { 0x3198, 0x01DFF, 0x01, "HdmiCec" }, + { 0x319C, 0x01DFF, 0x01, "HdmiIntDpHpd" }, + { 0x31A0, 0x019FF, 0x01, "SpdifOut" }, + { 0x31A4, 0x019FF, 0x01, "SpdifIn" }, + { 0x31A8, 0x01DFF, 0x01, "UsbVbusEn0" }, + { 0x31AC, 0x01DFF, 0x01, "UsbVbusEn1" }, + { 0x31B0, 0x019FF, 0x01, "DpHpd0" }, + { 0x31B4, 0x019FF, 0x00, "WifiEn" }, + { 0x31B8, 0x019FF, 0x00, "WifiRst" }, + { 0x31BC, 0x019FF, 0x00, "WifiWakeAp" }, + { 0x31C0, 0x019FF, 0x00, "ApWakeBt" }, + { 0x31C4, 0x019FF, 0x00, "BtRst" }, + { 0x31C8, 0x019FF, 0x00, "BtWakeAp" }, + { 0x31CC, 0x019FF, 0x00, "ApWakeNfc" }, + { 0x31D0, 0x019FF, 0x00, "NfcEn" }, + { 0x31D4, 0x019FF, 0x00, "NfcInt" }, + { 0x31D8, 0x019FF, 0x00, "GpsEn" }, + { 0x31DC, 0x019FF, 0x00, "GpsRst" }, + { 0x31E0, 0x019FF, 0x01, "CamRst" }, + { 0x31E4, 0x019FF, 0x02, "CamAfEn" }, + { 0x31E8, 0x019FF, 0x02, "CamFlashEn" }, + { 0x31EC, 0x019FF, 0x01, "Cam1Pwdn" }, + { 0x31F0, 0x019FF, 0x01, "Cam2Pwdn" }, + { 0x31F4, 0x019FF, 0x01, "Cam1Strobe" }, + { 0x31F8, 0x019FF, 0x01, "LcdTe" }, + { 0x31FC, 0x019FF, 0x03, "LcdBlPwm" }, + { 0x3200, 0x019FF, 0x00, "LcdBlEn" }, + { 0x3204, 0x019FF, 0x00, "LcdRst" }, + { 0x3208, 0x019FF, 0x01, "LcdGpio1" }, + { 0x320C, 0x019FF, 0x02, "LcdGpio2" }, + { 0x3210, 0x019FF, 0x00, "ApReady" }, + { 0x3214, 0x019FF, 0x00, "TouchRst" }, + { 0x3218, 0x019FF, 0x01, "TouchClk" }, + { 0x321C, 0x019FF, 0x00, "ModemWakeAp" }, + { 0x3220, 0x019FF, 0x00, "TouchInt" }, + { 0x3224, 0x019FF, 0x00, "MotionInt" }, + { 0x3228, 0x019FF, 0x00, "AlsProxInt" }, + { 0x322C, 0x019FF, 0x00, "TempAlert" }, + { 0x3230, 0x019FF, 0x00, "ButtonPowerOn" }, + { 0x3234, 0x019FF, 0x00, "ButtonVolUp" }, + { 0x3238, 0x019FF, 0x00, "ButtonVolDown" }, + { 0x323C, 0x019FF, 0x00, "ButtonSlideSw" }, + { 0x3240, 0x019FF, 0x00, "ButtonHome" }, + { 0x3244, 0x019FF, 0x01, "GpioPa6" }, + { 0x3248, 0x019FF, 0x00, "GpioPe6" }, + { 0x324C, 0x019FF, 0x00, "GpioPe7" }, + { 0x3250, 0x019FF, 0x00, "GpioPh6" }, + { 0x3254, 0x072FF, 0x02, "GpioPk0" }, + { 0x3258, 0x072FF, 0x02, "GpioPk1" }, + { 0x325C, 0x072FF, 0x02, "GpioPk2" }, + { 0x3260, 0x072FF, 0x02, "GpioPk3" }, + { 0x3264, 0x072FF, 0x01, "GpioPk4" }, + { 0x3268, 0x072FF, 0x01, "GpioPk5" }, + { 0x326C, 0x072FF, 0x01, "GpioPk6" }, + { 0x3270, 0x072FF, 0x01, "GpioPk7" }, + { 0x3274, 0x072FF, 0x00, "GpioPl0" }, + { 0x3278, 0x072FF, 0x01, "GpioPl1" }, + { 0x327C, 0x072FF, 0x01, "GpioPz0" }, + { 0x3280, 0x072FF, 0x02, "GpioPz1" }, + { 0x3284, 0x072FF, 0x02, "GpioPz2" }, + { 0x3288, 0x072FF, 0x01, "GpioPz3" }, + { 0x328C, 0x072FF, 0x01, "GpioPz4" }, + { 0x3290, 0x072FF, 0x01, "GpioPz5" }, + { 0x3294, 0x1F2FF, 0x02, "Sdmmc2Dat0" }, + { 0x3298, 0x1F2FF, 0x02, "Sdmmc2Dat1" }, + { 0x329C, 0x1F2FF, 0x02, "Sdmmc2Dat2" }, + { 0x32A0, 0x1F2FF, 0x02, "Sdmmc2Dat3" }, + { 0x32A4, 0x1F2FF, 0x02, "Sdmmc2Dat4" }, + { 0x32A8, 0x1F2FF, 0x02, "Sdmmc2Dat5" }, + { 0x32AC, 0x1F2FF, 0x02, "Sdmmc2Dat6" }, + { 0x32B0, 0x1F2FF, 0x02, "Sdmmc2Dat7" }, + { 0x32B4, 0x1F2FF, 0x02, "Sdmmc2Clk" }, + { 0x32B8, 0x1F2FF, 0x00, "Sdmmc2Clkb" }, + { 0x32BC, 0x1F2FF, 0x02, "Sdmmc2Cmd" }, + { 0x32C0, 0x1F2FF, 0x00, "Sdmmc2Dqs" }, + { 0x32C4, 0x1F2FF, 0x00, "Sdmmc2Dqsb" }, +}; + +constexpr inline size_t NumPinmuxPadCharacters = util::size(PinmuxPadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_index.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_index.hpp new file mode 100644 index 0000000..df2dc59 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_pad_index.hpp @@ -0,0 +1,348 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +#pragma once + +enum PinmuxPadIndex { + PinmuxPadIndex_Sdmmc1Clk = 0, + PinmuxPadIndex_Sdmmc1Cmd = 1, + PinmuxPadIndex_Sdmmc1Dat3 = 2, + PinmuxPadIndex_Sdmmc1Dat2 = 3, + PinmuxPadIndex_Sdmmc1Dat1 = 4, + PinmuxPadIndex_Sdmmc1Dat0 = 5, + PinmuxPadIndex_Sdmmc3Clk = 6, + PinmuxPadIndex_Sdmmc3Cmd = 7, + PinmuxPadIndex_Sdmmc3Dat0 = 8, + PinmuxPadIndex_Sdmmc3Dat1 = 9, + PinmuxPadIndex_Sdmmc3Dat2 = 10, + PinmuxPadIndex_Sdmmc3Dat3 = 11, + PinmuxPadIndex_PexL0RstN = 12, + PinmuxPadIndex_PexL0ClkreqN = 13, + PinmuxPadIndex_PexWakeN = 14, + PinmuxPadIndex_PexL1RstN = 15, + PinmuxPadIndex_PexL1ClkreqN = 16, + PinmuxPadIndex_SataLedActive = 17, + PinmuxPadIndex_Spi1Mosi = 18, + PinmuxPadIndex_Spi1Miso = 19, + PinmuxPadIndex_Spi1Sck = 20, + PinmuxPadIndex_Spi1Cs0 = 21, + PinmuxPadIndex_Spi1Cs1 = 22, + PinmuxPadIndex_Spi2Mosi = 23, + PinmuxPadIndex_Spi2Miso = 24, + PinmuxPadIndex_Spi2Sck = 25, + PinmuxPadIndex_Spi2Cs0 = 26, + PinmuxPadIndex_Spi2Cs1 = 27, + PinmuxPadIndex_Spi4Mosi = 28, + PinmuxPadIndex_Spi4Miso = 29, + PinmuxPadIndex_Spi4Sck = 30, + PinmuxPadIndex_Spi4Cs0 = 31, + PinmuxPadIndex_QspiSck = 32, + PinmuxPadIndex_QspiCsN = 33, + PinmuxPadIndex_QspiIo0 = 34, + PinmuxPadIndex_QspiIo1 = 35, + PinmuxPadIndex_QspiIo2 = 36, + PinmuxPadIndex_QspiIo3 = 37, + PinmuxPadIndex_Dmic1Clk = 38, + PinmuxPadIndex_Dmic1Dat = 39, + PinmuxPadIndex_Dmic2Clk = 40, + PinmuxPadIndex_Dmic2Dat = 41, + PinmuxPadIndex_Dmic3Clk = 42, + PinmuxPadIndex_Dmic3Dat = 43, + PinmuxPadIndex_Gen1I2cScl = 44, + PinmuxPadIndex_Gen1I2cSda = 45, + PinmuxPadIndex_Gen2I2cScl = 46, + PinmuxPadIndex_Gen2I2cSda = 47, + PinmuxPadIndex_Gen3I2cScl = 48, + PinmuxPadIndex_Gen3I2cSda = 49, + PinmuxPadIndex_CamI2cScl = 50, + PinmuxPadIndex_CamI2cSda = 51, + PinmuxPadIndex_PwrI2cScl = 52, + PinmuxPadIndex_PwrI2cSda = 53, + PinmuxPadIndex_Uart1Tx = 54, + PinmuxPadIndex_Uart1Rx = 55, + PinmuxPadIndex_Uart1Rts = 56, + PinmuxPadIndex_Uart1Cts = 57, + PinmuxPadIndex_Uart2Tx = 58, + PinmuxPadIndex_Uart2Rx = 59, + PinmuxPadIndex_Uart2Rts = 60, + PinmuxPadIndex_Uart2Cts = 61, + PinmuxPadIndex_Uart3Tx = 62, + PinmuxPadIndex_Uart3Rx = 63, + PinmuxPadIndex_Uart3Rts = 64, + PinmuxPadIndex_Uart3Cts = 65, + PinmuxPadIndex_Uart4Tx = 66, + PinmuxPadIndex_Uart4Rx = 67, + PinmuxPadIndex_Uart4Rts = 68, + PinmuxPadIndex_Uart4Cts = 69, + PinmuxPadIndex_Dap1Fs = 70, + PinmuxPadIndex_Dap1Din = 71, + PinmuxPadIndex_Dap1Dout = 72, + PinmuxPadIndex_Dap1Sclk = 73, + PinmuxPadIndex_Dap2Fs = 74, + PinmuxPadIndex_Dap2Din = 75, + PinmuxPadIndex_Dap2Dout = 76, + PinmuxPadIndex_Dap2Sclk = 77, + PinmuxPadIndex_Dap4Fs = 78, + PinmuxPadIndex_Dap4Din = 79, + PinmuxPadIndex_Dap4Dout = 80, + PinmuxPadIndex_Dap4Sclk = 81, + PinmuxPadIndex_Cam1Mclk = 82, + PinmuxPadIndex_Cam2Mclk = 83, + PinmuxPadIndex_JtagRtck = 84, + PinmuxPadIndex_Clk32kIn = 85, + PinmuxPadIndex_Clk32kOut = 86, + PinmuxPadIndex_BattBcl = 87, + PinmuxPadIndex_ClkReq = 88, + PinmuxPadIndex_CpuPwrReq = 89, + PinmuxPadIndex_PwrIntN = 90, + PinmuxPadIndex_Shutdown = 91, + PinmuxPadIndex_CorePwrReq = 92, + PinmuxPadIndex_AudMclk = 93, + PinmuxPadIndex_DvfsPwm = 94, + PinmuxPadIndex_DvfsClk = 95, + PinmuxPadIndex_GpioX1Aud = 96, + PinmuxPadIndex_GpioX3Aud = 97, + PinmuxPadIndex_GpioPcc7 = 98, + PinmuxPadIndex_HdmiCec = 99, + PinmuxPadIndex_HdmiIntDpHpd = 100, + PinmuxPadIndex_SpdifOut = 101, + PinmuxPadIndex_SpdifIn = 102, + PinmuxPadIndex_UsbVbusEn0 = 103, + PinmuxPadIndex_UsbVbusEn1 = 104, + PinmuxPadIndex_DpHpd0 = 105, + PinmuxPadIndex_WifiEn = 106, + PinmuxPadIndex_WifiRst = 107, + PinmuxPadIndex_WifiWakeAp = 108, + PinmuxPadIndex_ApWakeBt = 109, + PinmuxPadIndex_BtRst = 110, + PinmuxPadIndex_BtWakeAp = 111, + PinmuxPadIndex_ApWakeNfc = 112, + PinmuxPadIndex_NfcEn = 113, + PinmuxPadIndex_NfcInt = 114, + PinmuxPadIndex_GpsEn = 115, + PinmuxPadIndex_GpsRst = 116, + PinmuxPadIndex_CamRst = 117, + PinmuxPadIndex_CamAfEn = 118, + PinmuxPadIndex_CamFlashEn = 119, + PinmuxPadIndex_Cam1Pwdn = 120, + PinmuxPadIndex_Cam2Pwdn = 121, + PinmuxPadIndex_Cam1Strobe = 122, + PinmuxPadIndex_LcdTe = 123, + PinmuxPadIndex_LcdBlPwm = 124, + PinmuxPadIndex_LcdBlEn = 125, + PinmuxPadIndex_LcdRst = 126, + PinmuxPadIndex_LcdGpio1 = 127, + PinmuxPadIndex_LcdGpio2 = 128, + PinmuxPadIndex_ApReady = 129, + PinmuxPadIndex_TouchRst = 130, + PinmuxPadIndex_TouchClk = 131, + PinmuxPadIndex_ModemWakeAp = 132, + PinmuxPadIndex_TouchInt = 133, + PinmuxPadIndex_MotionInt = 134, + PinmuxPadIndex_AlsProxInt = 135, + PinmuxPadIndex_TempAlert = 136, + PinmuxPadIndex_ButtonPowerOn = 137, + PinmuxPadIndex_ButtonVolUp = 138, + PinmuxPadIndex_ButtonVolDown = 139, + PinmuxPadIndex_ButtonSlideSw = 140, + PinmuxPadIndex_ButtonHome = 141, + PinmuxPadIndex_GpioPa6 = 142, + PinmuxPadIndex_GpioPe6 = 143, + PinmuxPadIndex_GpioPe7 = 144, + PinmuxPadIndex_GpioPh6 = 145, + PinmuxPadIndex_GpioPk0 = 146, + PinmuxPadIndex_GpioPk1 = 147, + PinmuxPadIndex_GpioPk2 = 148, + PinmuxPadIndex_GpioPk3 = 149, + PinmuxPadIndex_GpioPk4 = 150, + PinmuxPadIndex_GpioPk5 = 151, + PinmuxPadIndex_GpioPk6 = 152, + PinmuxPadIndex_GpioPk7 = 153, + PinmuxPadIndex_GpioPl0 = 154, + PinmuxPadIndex_GpioPl1 = 155, + PinmuxPadIndex_GpioPz0 = 156, + PinmuxPadIndex_GpioPz1 = 157, + PinmuxPadIndex_GpioPz2 = 158, + PinmuxPadIndex_GpioPz3 = 159, + PinmuxPadIndex_GpioPz4 = 160, + PinmuxPadIndex_GpioPz5 = 161, + PinmuxPadIndex_Sdmmc2Dat0 = 162, + PinmuxPadIndex_Sdmmc2Dat1 = 163, + PinmuxPadIndex_Sdmmc2Dat2 = 164, + PinmuxPadIndex_Sdmmc2Dat3 = 165, + PinmuxPadIndex_Sdmmc2Dat4 = 166, + PinmuxPadIndex_Sdmmc2Dat5 = 167, + PinmuxPadIndex_Sdmmc2Dat6 = 168, + PinmuxPadIndex_Sdmmc2Dat7 = 169, + PinmuxPadIndex_Sdmmc2Clk = 170, + PinmuxPadIndex_Sdmmc2Clkb = 171, + PinmuxPadIndex_Sdmmc2Cmd = 172, + PinmuxPadIndex_Sdmmc2Dqs = 173, + PinmuxPadIndex_Sdmmc2Dqsb = 174, +}; + +enum PinmuxDrivePadIndex { + PinmuxDrivePadIndex_AlsProxInt = 0, + PinmuxDrivePadIndex_ApReady = 1, + PinmuxDrivePadIndex_ApWakeBt = 2, + PinmuxDrivePadIndex_ApWakeNfc = 3, + PinmuxDrivePadIndex_AudMclk = 4, + PinmuxDrivePadIndex_BattBcl = 5, + PinmuxDrivePadIndex_BtRst = 6, + PinmuxDrivePadIndex_BtWakeAp = 7, + PinmuxDrivePadIndex_ButtonHome = 8, + PinmuxDrivePadIndex_ButtonPowerOn = 9, + PinmuxDrivePadIndex_ButtonSlideSw = 10, + PinmuxDrivePadIndex_ButtonVolDown = 11, + PinmuxDrivePadIndex_ButtonVolUp = 12, + PinmuxDrivePadIndex_Cam1Mclk = 13, + PinmuxDrivePadIndex_Cam1Pwdn = 14, + PinmuxDrivePadIndex_Cam1Strobe = 15, + PinmuxDrivePadIndex_Cam2Mclk = 16, + PinmuxDrivePadIndex_Cam2Pwdn = 17, + PinmuxDrivePadIndex_CamAfEn = 18, + PinmuxDrivePadIndex_CamFlashEn = 19, + PinmuxDrivePadIndex_CamI2cScl = 20, + PinmuxDrivePadIndex_CamI2cSda = 21, + PinmuxDrivePadIndex_CamRst = 22, + PinmuxDrivePadIndex_Clk32kIn = 23, + PinmuxDrivePadIndex_Clk32kOut = 24, + PinmuxDrivePadIndex_ClkReq = 25, + PinmuxDrivePadIndex_CorePwrReq = 26, + PinmuxDrivePadIndex_CpuPwrReq = 27, + PinmuxDrivePadIndex_Dap1Din = 28, + PinmuxDrivePadIndex_Dap1Dout = 29, + PinmuxDrivePadIndex_Dap1Fs = 30, + PinmuxDrivePadIndex_Dap1Sclk = 31, + PinmuxDrivePadIndex_Dap2Din = 32, + PinmuxDrivePadIndex_Dap2Dout = 33, + PinmuxDrivePadIndex_Dap2Fs = 34, + PinmuxDrivePadIndex_Dap2Sclk = 35, + PinmuxDrivePadIndex_Dap4Din = 36, + PinmuxDrivePadIndex_Dap4Dout = 37, + PinmuxDrivePadIndex_Dap4Fs = 38, + PinmuxDrivePadIndex_Dap4Sclk = 39, + PinmuxDrivePadIndex_Dmic1Clk = 40, + PinmuxDrivePadIndex_Dmic1Dat = 41, + PinmuxDrivePadIndex_Dmic2Clk = 42, + PinmuxDrivePadIndex_Dmic2Dat = 43, + PinmuxDrivePadIndex_Dmic3Clk = 44, + PinmuxDrivePadIndex_Dmic3Dat = 45, + PinmuxDrivePadIndex_DpHpd = 46, + PinmuxDrivePadIndex_DvfsClk = 47, + PinmuxDrivePadIndex_DvfsPwm = 48, + PinmuxDrivePadIndex_Gen1I2cScl = 49, + PinmuxDrivePadIndex_Gen1I2cSda = 50, + PinmuxDrivePadIndex_Gen2I2cScl = 51, + PinmuxDrivePadIndex_Gen2I2cSda = 52, + PinmuxDrivePadIndex_Gen3I2cScl = 53, + PinmuxDrivePadIndex_Gen3I2cSda = 54, + PinmuxDrivePadIndex_GpioPa6 = 55, + PinmuxDrivePadIndex_GpioPcc7 = 56, + PinmuxDrivePadIndex_GpioPe6 = 57, + PinmuxDrivePadIndex_GpioPe7 = 58, + PinmuxDrivePadIndex_GpioPh6 = 59, + PinmuxDrivePadIndex_GpioPk0 = 60, + PinmuxDrivePadIndex_GpioPk1 = 61, + PinmuxDrivePadIndex_GpioPk2 = 62, + PinmuxDrivePadIndex_GpioPk3 = 63, + PinmuxDrivePadIndex_GpioPk4 = 64, + PinmuxDrivePadIndex_GpioPk5 = 65, + PinmuxDrivePadIndex_GpioPk6 = 66, + PinmuxDrivePadIndex_GpioPk7 = 67, + PinmuxDrivePadIndex_GpioPl0 = 68, + PinmuxDrivePadIndex_GpioPl1 = 69, + PinmuxDrivePadIndex_GpioPz0 = 70, + PinmuxDrivePadIndex_GpioPz1 = 71, + PinmuxDrivePadIndex_GpioPz2 = 72, + PinmuxDrivePadIndex_GpioPz3 = 73, + PinmuxDrivePadIndex_GpioPz4 = 74, + PinmuxDrivePadIndex_GpioPz5 = 75, + PinmuxDrivePadIndex_GpioX1Aud = 76, + PinmuxDrivePadIndex_GpioX3Aud = 77, + PinmuxDrivePadIndex_GpsEn = 78, + PinmuxDrivePadIndex_GpsRst = 79, + PinmuxDrivePadIndex_HdmiCec = 80, + PinmuxDrivePadIndex_HdmiIntDpHpd = 81, + PinmuxDrivePadIndex_JtagRtck = 82, + PinmuxDrivePadIndex_LcdBlEn = 83, + PinmuxDrivePadIndex_LcdBlPwm = 84, + PinmuxDrivePadIndex_LcdGpio1 = 85, + PinmuxDrivePadIndex_LcdGpio2 = 86, + PinmuxDrivePadIndex_LcdRst = 87, + PinmuxDrivePadIndex_LcdTe = 88, + PinmuxDrivePadIndex_ModemWakeAp = 89, + PinmuxDrivePadIndex_MotionInt = 90, + PinmuxDrivePadIndex_NfcEn = 91, + PinmuxDrivePadIndex_NfcInt = 92, + PinmuxDrivePadIndex_PexL0ClkReqN = 93, + PinmuxDrivePadIndex_PexL0RstN = 94, + PinmuxDrivePadIndex_PexL1ClkreqN = 95, + PinmuxDrivePadIndex_PexL1RstN = 96, + PinmuxDrivePadIndex_PexWakeN = 97, + PinmuxDrivePadIndex_PwrI2cScl = 98, + PinmuxDrivePadIndex_PwrI2cSda = 99, + PinmuxDrivePadIndex_PwrIntN = 100, + PinmuxDrivePadIndex_QspiComp = 101, + PinmuxDrivePadIndex_QspiSck = 102, + PinmuxDrivePadIndex_SataLedActive = 103, + PinmuxDrivePadIndex_Sdmmc1Pad = 104, + PinmuxDrivePadIndex_Sdmmc3Pad = 105, + PinmuxDrivePadIndex_Shutdown = 106, + PinmuxDrivePadIndex_SpdifIn = 107, + PinmuxDrivePadIndex_SpdifOut = 108, + PinmuxDrivePadIndex_Spi1Cs0 = 109, + PinmuxDrivePadIndex_Spi1Cs1 = 110, + PinmuxDrivePadIndex_Spi1Miso = 111, + PinmuxDrivePadIndex_Spi1Mosi = 112, + PinmuxDrivePadIndex_Spi1Sck = 113, + PinmuxDrivePadIndex_Spi2Cs0 = 114, + PinmuxDrivePadIndex_Spi2Cs1 = 115, + PinmuxDrivePadIndex_Spi2Miso = 116, + PinmuxDrivePadIndex_Spi2Mosi = 117, + PinmuxDrivePadIndex_Spi2Sck = 118, + PinmuxDrivePadIndex_Spi4Cs0 = 119, + PinmuxDrivePadIndex_Spi4Miso = 120, + PinmuxDrivePadIndex_Spi4Mosi = 121, + PinmuxDrivePadIndex_Spi4Sck = 122, + PinmuxDrivePadIndex_TempAlert = 123, + PinmuxDrivePadIndex_TouchClk = 124, + PinmuxDrivePadIndex_TouchInt = 125, + PinmuxDrivePadIndex_TouchRst = 126, + PinmuxDrivePadIndex_Uart1Cts = 127, + PinmuxDrivePadIndex_Uart1Rts = 128, + PinmuxDrivePadIndex_Uart1Rx = 129, + PinmuxDrivePadIndex_Uart1Tx = 130, + PinmuxDrivePadIndex_Uart2Cts = 131, + PinmuxDrivePadIndex_Uart2Rts = 132, + PinmuxDrivePadIndex_Uart2Rx = 133, + PinmuxDrivePadIndex_Uart2Tx = 134, + PinmuxDrivePadIndex_Uart3Cts = 135, + PinmuxDrivePadIndex_Uart3Rts = 136, + PinmuxDrivePadIndex_Uart3Rx = 137, + PinmuxDrivePadIndex_Uart3Tx = 138, + PinmuxDrivePadIndex_Uart4Cts = 139, + PinmuxDrivePadIndex_Uart4Rts = 140, + PinmuxDrivePadIndex_Uart4Rx = 141, + PinmuxDrivePadIndex_Uart4Tx = 142, + PinmuxDrivePadIndex_UsbVbusEn0 = 143, + PinmuxDrivePadIndex_UsbVbusEn1 = 144, + PinmuxDrivePadIndex_WifiEn = 145, + PinmuxDrivePadIndex_WifiRst = 146, + PinmuxDrivePadIndex_WifiWakeAp = 147, +}; diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.cpp new file mode 100644 index 0000000..7efce9d --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.cpp @@ -0,0 +1,641 @@ +/* + * 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 "pinmux_pad_index.hpp" +#include "pinmux_board_driver_api.hpp" +#include "pinmux_platform_pads.hpp" + +namespace ams::pinmux::driver::board::nintendo_nx { + + namespace { + + uintptr_t g_apb_misc_virtual_address = dd::QueryIoMapping(0x70000000, 0x4000); + + enum PinmuxPadMask : u32 { + PinmuxPadMask_Pm = 0x3, + PinmuxPadMask_Pupd = 0xC, + PinmuxPadMask_Tristate = 0x10, + PinmuxPadMask_Park = 0x20, + PinmuxPadMask_EInput = 0x40, + PinmuxPadMask_Lock = 0x80, + PinmuxPadMask_ELpdr = 0x100, + PinmuxPadMask_EHsm = 0x200, + PinmuxPadMask_EIoHv = 0x400, + PinmuxPadMask_EOd = 0x800, + PinmuxPadMask_ESchmt = 0x1000, + PinmuxPadMask_DrvType = 0x6000, + PinmuxPadMask_Preemp = 0x8000, + PinmuxPadMask_IoReset = 0x10000, + }; + + enum PinmuxPadBitOffset : u32 { + PinmuxPadBitOffset_Pm = 0x0, + PinmuxPadBitOffset_Pupd = 0x2, + PinmuxPadBitOffset_Tristate = 0x4, + PinmuxPadBitOffset_Park = 0x5, + PinmuxPadBitOffset_EInput = 0x6, + PinmuxPadBitOffset_Lock = 0x7, + PinmuxPadBitOffset_ELpdr = 0x8, + PinmuxPadBitOffset_EHsm = 0x9, + PinmuxPadBitOffset_EIoHv = 0xA, + PinmuxPadBitOffset_EOd = 0xB, + PinmuxPadBitOffset_ESchmt = 0xC, + PinmuxPadBitOffset_DrvType = 0xD, + PinmuxPadBitOffset_Preemp = 0xF, + PinmuxPadBitOffset_IoReset = 0x10, + }; + + enum PinmuxOptBitMask : u32 { + PinmuxOptBitMask_Pm = 0x7, + PinmuxOptBitMask_Pupd = 0x18, + PinmuxOptBitMask_Dir = 0x60, + PinmuxOptBitMask_Lock = 0x80, + PinmuxOptBitMask_IoReset = 0x100, + PinmuxOptBitMask_IoHv = 0x200, + PinmuxOptBitMask_Park = 0x400, + PinmuxOptBitMask_Lpdr = 0x800, + PinmuxOptBitMask_Hsm = 0x1000, + PinmuxOptBitMask_Schmt = 0x2000, + PinmuxOptBitMask_DrvType = 0xC000, + PinmuxOptBitMask_Preemp = 0x10000, + }; + + enum PinmuxOptBitOffset { + PinmuxOptBitOffset_Pm = 0x0, + PinmuxOptBitOffset_Pupd = 0x3, + PinmuxOptBitOffset_Dir = 0x5, + PinmuxOptBitOffset_Lock = 0x7, + PinmuxOptBitOffset_IoReset = 0x8, + PinmuxOptBitOffset_IoHv = 0x9, + PinmuxOptBitOffset_Park = 0xA, + PinmuxOptBitOffset_Lpdr = 0xB, + PinmuxOptBitOffset_Hsm = 0xC, + PinmuxOptBitOffset_Schmt = 0xD, + PinmuxOptBitOffset_DrvType = 0xE, + PinmuxOptBitOffset_Preemp = 0x10, + }; + + enum PinmuxDrivePadMask : u32{ + PinmuxDrivePadMask_DrvDn = 0x0001F000, + PinmuxDrivePadMask_DrvUp = 0x01F00000, + PinmuxDrivePadMask_CzDrvDn = 0x0007F000, + PinmuxDrivePadMask_CzDrvUp = 0x07F00000, + PinmuxDrivePadMask_SlwR = 0x30000000, + PinmuxDrivePadMask_SlwF = 0xC0000000, + }; + + enum PinmuxDrivePadBitOffset : u32 { + PinmuxDrivePadBitOffset_DrvDn = 12, + PinmuxDrivePadBitOffset_DrvUp = 20, + PinmuxDrivePadBitOffset_CzDrvDn = 12, + PinmuxDrivePadBitOffset_CzDrvUp = 20, + PinmuxDrivePadBitOffset_SlwR = 28, + PinmuxDrivePadBitOffset_SlwF = 30, + }; + + enum PinmuxDriveOptBitMask : u32 { + PinmuxDriveOptBitMask_DrvDn = 0x0001F000, + PinmuxDriveOptBitMask_DrvUp = 0x01F00000, + PinmuxDriveOptBitMask_CzDrvDn = 0x0007F000, + PinmuxDriveOptBitMask_CzDrvUp = 0x07F00000, + PinmuxDriveOptBitMask_SlwR = 0x30000000, + PinmuxDriveOptBitMask_SlwF = 0xC0000000, + }; + + enum PinmuxDriveOptBitOffset : u32 { + PinmuxDriveOptBitOffset_DrvDn = 12, + PinmuxDriveOptBitOffset_DrvUp = 20, + PinmuxDriveOptBitOffset_CzDrvDn = 12, + PinmuxDriveOptBitOffset_CzDrvUp = 20, + PinmuxDriveOptBitOffset_SlwR = 28, + PinmuxDriveOptBitOffset_SlwF = 30, + }; + + enum PinmuxOpt : u32 { + /* Pm */ + PinmuxOpt_Gpio = 0x4, + PinmuxOpt_Unused = 0x5, + + /* Pupd */ + PinmuxOpt_NoPupd = 0x0, + PinmuxOpt_PullDown = 0x8, + PinmuxOpt_PullUp = 0x10, + + /* Dir */ + PinmuxOpt_Output = 0x0, + PinmuxOpt_Input = 0x20, + PinmuxOpt_Bidirection = 0x40, + PinmuxOpt_OpenDrain = 0x60, + + /* Lock */ + PinmuxOpt_Unlock = 0x0, + PinmuxOpt_Lock = 0x80, + + /* IoReset */ + PinmuxOpt_DisableIoReset = 0x0, + PinmuxOpt_EnableIoReset = 0x100, + + /* IoHv */ + PinmuxOpt_NormalVoltage = 0x0, + PinmuxOpt_HighVoltage = 0x200, + + /* Park */ + PinmuxOpt_ResetOnLowPower = 0x0, + PinmuxOpt_ParkOnLowPower = 0x400, + + /* Lpdr */ + PinmuxOpt_DisableBaseDriver = 0x0, + PinmuxOpt_EnableBaseDriver = 0x800, + + /* Hsm */ + PinmuxOpt_DisableHighSpeedMode = 0x0, + PinmuxOpt_EnableHighSpeedMode = 0x1000, + + /* Schmt */ + PinmuxOpt_CmosMode = 0x0, + PinmuxOpt_SchmittTrigger = 0x2000, + + /* DrvType */ + PinmuxOpt_DrvType1X = 0x0, + PinmuxOpt_DrvType2X = 0x4000, + PinmuxOpt_DrvType3X = 0x8000, + PinmuxOpt_DrvType4X = 0xC000, + + /* Preemp */ + PinmuxOpt_DisablePreemp = 0x0, + PinmuxOpt_EnablePreemp = 0x10000, + }; + + enum PinmuxPadPm : u32 { + PinmuxPadPm_Default = 0xFFFFFFFF, + PinmuxPadPm_Pm0 = 0x0, + PinmuxPadPm_Pm1 = 0x1, + PinmuxPadPm_Pm2 = 0x2, + PinmuxPadPm_Pm3 = 0x3, + PinmuxPadPm_Safe = 0x4, + }; + + struct PinmuxPadCharacter { + u32 reg_offset; + u32 reg_mask; + u8 safe_func; + const char *pad_name; + }; + + struct PinmuxDrivePadCharacter { + u32 reg_offset; + u32 reg_mask; + const char *pad_name; + }; + + #include "pinmux_pad_characters.inc" + #include "pinmux_drive_pad_characters.inc" + + class PinmuxPad { + private: + u32 reg_address; + u32 reg_mask; + u32 reg_value; + u8 safe_func; + const char *pad_name; + private: + bool IsValidRegisterAddress() const { + return this->reg_address - 0x70003000 <= 0x2C4; + } + + uintptr_t GetRegisterAddress() const { + return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); + } + + bool UpdateBits(u32 value, u32 offset, u32 mask) { + if ((this->reg_mask & mask) != 0) { + if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { + this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); + } + return true; + } else { + return false; + } + } + + u32 ReadReg() const { + if (this->IsValidRegisterAddress()) { + return reg::Read(this->GetRegisterAddress()); + } else { + return 0; + } + } + + void WriteReg() const { + if (this->IsValidRegisterAddress()) { + reg::Write(this->GetRegisterAddress(), this->reg_value); + } + } + + bool IsLocked() const { + return (this->reg_value & PinmuxPadMask_Lock) != 0; + } + + void UpdatePm(u8 v) { + if (v != 0xFF) { + if (v == PinmuxPadPm_Safe) { + v = this->safe_func; + } + this->UpdateBits(v, PinmuxPadBitOffset_Pm, PinmuxPadMask_Pm); + } + } + + void UpdatePupd(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Pupd, PinmuxPadMask_Pupd); + } + } + + void UpdateTristate(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Tristate, PinmuxPadMask_Tristate); + } + } + + void UpdateEInput(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EInput, PinmuxPadMask_EInput); + } + } + + void UpdateEOd(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EOd, PinmuxPadMask_EOd); + } + } + + void UpdateLock(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Lock, PinmuxPadMask_Lock); + } + } + + void UpdateIoReset(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_IoReset, PinmuxPadMask_IoReset); + } + } + + void UpdatePark(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Park, PinmuxPadMask_Park); + } + } + + void UpdateELpdr(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_ELpdr, PinmuxPadMask_ELpdr); + } + } + + void UpdateEHsm(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EHsm, PinmuxPadMask_EHsm); + } + } + + void UpdateEIoHv(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EIoHv, PinmuxPadMask_EIoHv); + } + } + + void UpdateESchmt(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_ESchmt, PinmuxPadMask_ESchmt); + } + } + + void UpdatePreemp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Preemp, PinmuxPadMask_Preemp); + } + } + + void UpdateDrvType(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_DrvType, PinmuxPadMask_DrvType); + } + } + public: + constexpr PinmuxPad() : reg_address(), reg_mask(), reg_value(), safe_func(), pad_name() { /* ... */ } + + void UpdatePinmuxPad(u32 config, u32 config_mask) { + /* Update register value. */ + this->reg_value = this->ReadReg(); + + /* Check if we're locked. */ + if (this->IsLocked()) { + return; + } + + /* Update PM. */ + if ((config_mask & PinmuxOptBitMask_Pm) != 0) { + const auto opt = (config & PinmuxOptBitMask_Pm); + u8 pm = PinmuxPadPm_Safe; + if (opt != PinmuxOpt_Gpio) { + if (opt == PinmuxOpt_Unused) { + this->UpdatePupd(true); + this->UpdateTristate(true); + this->UpdateEInput(0); + } else if (opt <= PinmuxOpt_Unused) { + pm = opt >> PinmuxOptBitOffset_Pm; + } + } + this->UpdatePm(pm); + } + + /* Update pupd. */ + if ((config_mask & PinmuxOptBitMask_Pupd) != 0) { + const auto opt = (config & PinmuxOptBitMask_Pupd); + if (opt == PinmuxOpt_NoPupd || opt == PinmuxOpt_PullDown || opt == PinmuxOpt_PullUp) { + this->UpdatePupd(opt >> PinmuxOptBitOffset_Pupd); + } + } + + /* Update direction. */ + if ((config_mask & PinmuxOptBitMask_Dir) != 0) { + const auto opt = (config & PinmuxOptBitMask_Dir); + if (opt == PinmuxOpt_Output) { + this->UpdateTristate(false); + this->UpdateEInput(false); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_Input) { + this->UpdateTristate(true); + this->UpdateEInput(true); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_Bidirection) { + this->UpdateTristate(false); + this->UpdateEInput(true); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_OpenDrain) { + this->UpdateTristate(false); + this->UpdateEInput(true); + this->UpdateEOd(true); + } + } + + /* Update Lock. */ + if ((config_mask & PinmuxOptBitMask_Lock) != 0) { + const auto opt = (config & PinmuxOptBitMask_Lock); + this->UpdateLock(opt != 0); + } + + /* Update IoReset. */ + if ((config_mask & PinmuxOptBitMask_IoReset) != 0) { + const auto opt = (config & PinmuxOptBitMask_IoReset); + this->UpdateIoReset(opt != 0); + } + + /* Update Park. */ + if ((config_mask & PinmuxOptBitMask_Park) != 0) { + const auto opt = (config & PinmuxOptBitMask_Park); + this->UpdatePark(opt != 0); + } + + /* Update Lpdr. */ + if ((config_mask & PinmuxOptBitMask_Lpdr) != 0) { + const auto opt = (config & PinmuxOptBitMask_Lpdr); + this->UpdateELpdr(opt != 0); + } + + /* Update Hsm. */ + if ((config_mask & PinmuxOptBitMask_Hsm) != 0) { + const auto opt = (config & PinmuxOptBitMask_Hsm); + this->UpdateEHsm(opt != 0); + } + + /* Update IoHv. */ + if ((config_mask & PinmuxOptBitMask_IoHv) != 0) { + const auto opt = (config & PinmuxOptBitMask_IoHv); + this->UpdateEIoHv(opt != 0); + } + + /* Update Schmt. */ + if ((config_mask & PinmuxOptBitMask_Schmt) != 0) { + const auto opt = (config & PinmuxOptBitMask_Schmt); + this->UpdateESchmt(opt != 0); + } + + /* Update Preemp. */ + if ((config_mask & PinmuxOptBitMask_Preemp) != 0) { + const auto opt = (config & PinmuxOptBitMask_Preemp); + this->UpdatePreemp(opt != 0); + } + + /* Update drive type. */ + if ((config_mask & PinmuxOptBitMask_DrvType) != 0) { + const auto opt = (config & PinmuxOptBitMask_DrvType); + this->UpdateDrvType(opt >> PinmuxOptBitOffset_DrvType); + } + + /* Write the updated register value. */ + this->WriteReg(); + } + + void SetCharacter(const PinmuxPadCharacter &character) { + this->reg_address = character.reg_offset + 0x70000000; + this->reg_mask = character.reg_mask; + this->safe_func = character.safe_func; + this->reg_value = this->ReadReg(); + this->pad_name = character.pad_name; + + if ((this->reg_mask & this->reg_value & PinmuxPadMask_Park) != 0) { + this->reg_value &= ~(PinmuxPadMask_Park); + } + + this->WriteReg(); + } + }; + + class PinmuxDrivePad { + private: + u32 reg_address; + u32 reg_mask; + u32 reg_value; + u8 safe_func; + const char *pad_name; + private: + bool IsValidRegisterAddress() const { + return this->reg_address - 0x700008E4 <= 0x288; + } + + uintptr_t GetRegisterAddress() const { + return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); + } + + bool UpdateBits(u32 value, u32 offset, u32 mask) { + if ((this->reg_mask & mask) != 0) { + if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { + this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); + } + return true; + } else { + return false; + } + } + + u32 ReadReg() const { + if (this->IsValidRegisterAddress()) { + return reg::Read(this->GetRegisterAddress()); + } else { + return 0; + } + } + + void WriteReg() const { + if (this->IsValidRegisterAddress()) { + reg::Write(this->GetRegisterAddress(), this->reg_value); + } + } + + bool IsCzDrvDn() const { + return (this->reg_mask & PinmuxDrivePadMask_CzDrvDn) == PinmuxDrivePadMask_CzDrvDn; + } + + bool IsCzDrvUp() const { + return (this->reg_mask & PinmuxDrivePadMask_CzDrvUp) == PinmuxDrivePadMask_CzDrvUp; + } + + void UpdateDrvDn(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvDn, PinmuxDrivePadMask_DrvDn); + } + } + + void UpdateDrvUp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvUp, PinmuxDrivePadMask_DrvUp); + } + } + + void UpdateCzDrvDn(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvDn, PinmuxDrivePadMask_CzDrvDn); + } + } + + void UpdateCzDrvUp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvUp, PinmuxDrivePadMask_CzDrvUp); + } + } + + void UpdateSlwR(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwR, PinmuxDrivePadMask_SlwR); + } + } + + void UpdateSlwF(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwF, PinmuxDrivePadMask_SlwF); + } + } + public: + constexpr PinmuxDrivePad() : reg_address(), reg_mask(), reg_value(), pad_name() { /* ... */ } + + void UpdatePinmuxDrivePad(u32 config, u32 config_mask) { + /* Update register value. */ + this->reg_value = this->ReadReg(); + + /* Update drvdn. */ + if ((config_mask & PinmuxDriveOptBitMask_DrvDn) != 0) { + if (this->IsCzDrvDn()) { + const auto opt = (config & PinmuxDriveOptBitMask_CzDrvDn); + this->UpdateCzDrvDn(opt >> PinmuxDriveOptBitOffset_CzDrvDn); + } else { + const auto opt = (config & PinmuxDriveOptBitMask_DrvDn); + this->UpdateDrvDn(opt >> PinmuxDriveOptBitOffset_DrvDn); + } + } + + /* Update drvup. */ + if ((config_mask & PinmuxDriveOptBitMask_DrvUp) != 0) { + if (this->IsCzDrvUp()) { + const auto opt = (config & PinmuxDriveOptBitMask_CzDrvUp); + this->UpdateCzDrvUp(opt >> PinmuxDriveOptBitOffset_CzDrvUp); + } else { + const auto opt = (config & PinmuxDriveOptBitMask_DrvUp); + this->UpdateDrvUp(opt >> PinmuxDriveOptBitOffset_DrvUp); + } + } + + /* Update slwr */ + if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { + const auto opt = (config & PinmuxDriveOptBitMask_SlwR); + this->UpdateSlwR(opt >> PinmuxDriveOptBitOffset_SlwR); + } + + /* Update slwf */ + if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { + const auto opt = (config & PinmuxDriveOptBitMask_SlwF); + this->UpdateSlwF(opt >> PinmuxDriveOptBitOffset_SlwF); + } + + /* Write the updated register value. */ + this->WriteReg(); + } + + void SetCharacter(const PinmuxDrivePadCharacter &character) { + this->reg_address = character.reg_offset + 0x70000000; + this->reg_mask = character.reg_mask; + this->reg_value = this->ReadReg(); + this->pad_name = character.pad_name; + } + }; + + constinit std::array g_pinmux_pads{}; + constinit std::array g_pinmux_drive_pads{}; + + } + + void InitializePlatformPads() { + /* Initialize all pads. */ + for (size_t i = 0; i < NumPinmuxPadCharacters; ++i) { + g_pinmux_pads[i].SetCharacter(PinmuxPadCharacters[i]); + } + + /* Update all drive pads. */ + for (size_t i = 0; i < NumPinmuxDrivePadCharacters; ++i) { + g_pinmux_drive_pads[i].SetCharacter(PinmuxDrivePadCharacters[i]); + } + } + + void UpdateSinglePinmuxPad(const PinmuxPadConfig &config) { + if (IsInitialized()) { + g_pinmux_pads[config.index].UpdatePinmuxPad(config.option, config.option_mask); + } + } + + void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config) { + if (IsInitialized()) { + g_pinmux_drive_pads[config.index].UpdatePinmuxDrivePad(config.option, config.option_mask); + } + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.hpp new file mode 100644 index 0000000..144bf52 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo/nx/pinmux_platform_pads.hpp @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pinmux::driver::board::nintendo_nx { + + struct PinmuxPadConfig { + u32 index; + u32 option; + u32 option_mask; + }; + + struct PinmuxDrivePadConfig { + u32 index; + u32 option; + u32 option_mask; + }; + + void InitializePlatformPads(); + + void UpdateSinglePinmuxPad(const PinmuxPadConfig &config); + void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config); + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp deleted file mode 100644 index fc35ce9..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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 "pinmux_pad_index.hpp" -#include "pinmux_board_driver_api.hpp" -#include "pinmux_platform_pads.hpp" - -namespace ams::pinmux::driver::board::nintendo_nx { - - namespace { - - constinit bool g_initialized = false; - - #include "pinmux_initial_pad_config_icosa.inc" - #include "pinmux_initial_pad_config_hoag.inc" - #include "pinmux_initial_pad_config_iowa.inc" - #include "pinmux_initial_pad_config_calcio.inc" - #include "pinmux_initial_pad_config_aula.inc" - - #include "pinmux_initial_drive_pad_config.inc" - #include "pinmux_initial_drive_pad_config_hoag.inc" - - } - - bool IsInitialized() { - return g_initialized; - } - - void Initialize() { - InitializePlatformPads(); - g_initialized = true; - } - - void Finalize() { - /* ... */ - } - - void SetInitialConfig() { - const PinmuxPadConfig *configs = nullptr; - size_t num_configs = 0; - bool is_mariko = false; - switch (spl::GetHardwareType()) { - case spl::HardwareType::Icosa: - configs = PinmuxPadConfigsIcosa; - num_configs = NumPinmuxPadConfigsIcosa; - is_mariko = false; - break; - case spl::HardwareType::Hoag: - configs = PinmuxPadConfigsHoag; - num_configs = NumPinmuxPadConfigsHoag; - is_mariko = true; - break; - case spl::HardwareType::Iowa: - configs = PinmuxPadConfigsIowa; - num_configs = NumPinmuxPadConfigsIowa; - is_mariko = true; - break; - case spl::HardwareType::Calcio: - configs = PinmuxPadConfigsCalcio; - num_configs = NumPinmuxPadConfigsCalcio; - is_mariko = true; - break; - case spl::HardwareType::Aula: - configs = PinmuxPadConfigsAula; - num_configs = NumPinmuxPadConfigsAula; - is_mariko = true; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; ++i) { - UpdateSinglePinmuxPad(configs[i]); - } - - if (is_mariko) { - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Clk, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Cmd, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat0, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat1, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat2, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat3, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat4, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat5, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat6, 0x2000, 0x2000 }); - UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat7, 0x2000, 0x2000 }); - } - } - - void SetInitialDrivePadConfig() { - const PinmuxDrivePadConfig *configs = nullptr; - size_t num_configs = 0; - switch (spl::GetHardwareType()) { - case spl::HardwareType::Icosa: - configs = PinmuxDrivePadConfigs; - num_configs = NumPinmuxDrivePadConfigs; - break; - case spl::HardwareType::Hoag: - configs = PinmuxDrivePadConfigsHoag; - num_configs = NumPinmuxDrivePadConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = PinmuxDrivePadConfigs; - num_configs = NumPinmuxDrivePadConfigs; - break; - case spl::HardwareType::Calcio: - configs = PinmuxDrivePadConfigs; - num_configs = NumPinmuxDrivePadConfigs; - break; - case spl::HardwareType::Aula: - configs = PinmuxDrivePadConfigs; - num_configs = NumPinmuxDrivePadConfigs; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; ++i) { - UpdateSinglePinmuxDrivePad(configs[i]); - } - } - -} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp deleted file mode 100644 index 48be15d..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::pinmux::driver::board::nintendo_nx { - - bool IsInitialized(); - - void Initialize(); - void Finalize(); - - void SetInitialConfig(); - void SetInitialDrivePadConfig(); - -} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc deleted file mode 100644 index a6ed61f..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ - -constexpr inline const PinmuxDrivePadCharacter PinmuxDrivePadCharacters[] = { - { 0x08E4, 0x01F1F000, "AlsProxInt" }, - { 0x08E8, 0x01F1F000, "ApReady" }, - { 0x08EC, 0x01F1F000, "ApWakeBt" }, - { 0x08F0, 0x01F1F000, "ApWakeNfc" }, - { 0x08F4, 0x01F1F000, "AudMclk" }, - { 0x08F8, 0x01F1F000, "BattBcl" }, - { 0x08FC, 0x01F1F000, "BtRst" }, - { 0x0900, 0x01F1F000, "BtWakeAp" }, - { 0x0904, 0x01F1F000, "ButtonHome" }, - { 0x0908, 0x01F1F000, "ButtonPowerOn" }, - { 0x090C, 0x01F1F000, "ButtonSlideSw" }, - { 0x0910, 0x01F1F000, "ButtonVolDown" }, - { 0x0914, 0x01F1F000, "ButtonVolUp" }, - { 0x0918, 0x01F1F000, "Cam1Mclk" }, - { 0x091C, 0x01F1F000, "Cam1Pwdn" }, - { 0x0920, 0x01F1F000, "Cam1Strobe" }, - { 0x0924, 0x01F1F000, "Cam2Mclk" }, - { 0x0928, 0x01F1F000, "Cam2Pwdn" }, - { 0x092C, 0x01F1F000, "CamAfEn" }, - { 0x0930, 0x01F1F000, "CamFlashEn" }, - { 0x0934, 0x01F1F000, "CamI2cScl" }, - { 0x0938, 0x01F1F000, "CamI2cSda" }, - { 0x093C, 0x01F1F000, "CamRst" }, - { 0x0940, 0x01F1F000, "Clk32kIn" }, - { 0x0944, 0x01F1F000, "Clk32kOut" }, - { 0x0948, 0x01F1F000, "ClkReq" }, - { 0x094C, 0x01F1F000, "CorePwrReq" }, - { 0x0950, 0x01F1F000, "CpuPwrReq" }, - { 0x0954, 0xF0000000, "Dap1Din" }, - { 0x0958, 0xF0000000, "Dap1Dout" }, - { 0x095C, 0xF0000000, "Dap1Fs" }, - { 0x0960, 0xF0000000, "Dap1Sclk" }, - { 0x0964, 0xF0000000, "Dap2Din" }, - { 0x0968, 0xF0000000, "Dap2Dout" }, - { 0x096C, 0xF0000000, "Dap2Fs" }, - { 0x0970, 0xF0000000, "Dap2Sclk" }, - { 0x0974, 0x01F1F000, "Dap4Din" }, - { 0x0978, 0x01F1F000, "Dap4Dout" }, - { 0x097C, 0x01F1F000, "Dap4Fs" }, - { 0x0980, 0x01F1F000, "Dap4Sclk" }, - { 0x0984, 0x01F1F000, "Dmic1Clk" }, - { 0x0988, 0x01F1F000, "Dmic1Dat" }, - { 0x098C, 0x01F1F000, "Dmic2Clk" }, - { 0x0990, 0x01F1F000, "Dmic2Dat" }, - { 0x0994, 0x01F1F000, "Dmic3Clk" }, - { 0x0998, 0x01F1F000, "Dmic3Dat" }, - { 0x099C, 0x01F1F000, "DpHpd" }, - { 0x09A0, 0x01F1F000, "DvfsClk" }, - { 0x09A4, 0x01F1F000, "DvfsPwm" }, - { 0x09A8, 0x01F1F000, "Gen1I2cScl" }, - { 0x09AC, 0x01F1F000, "Gen1I2cSda" }, - { 0x09B0, 0x01F1F000, "Gen2I2cScl" }, - { 0x09B4, 0x01F1F000, "Gen2I2cSda" }, - { 0x09B8, 0x01F1F000, "Gen3I2cScl" }, - { 0x09BC, 0x01F1F000, "Gen3I2cSda" }, - { 0x09C0, 0x01F1F000, "GpioPa6" }, - { 0x09C4, 0x01F1F000, "GpioPcc7" }, - { 0x09C8, 0x01F1F000, "GpioPe6" }, - { 0x09CC, 0x01F1F000, "GpioPe7" }, - { 0x09D0, 0x01F1F000, "GpioPh6" }, - { 0x09D4, 0xF0000000, "GpioPk0" }, - { 0x09D8, 0xF0000000, "GpioPk1" }, - { 0x09DC, 0xF0000000, "GpioPk2" }, - { 0x09E0, 0xF0000000, "GpioPk3" }, - { 0x09E4, 0xF0000000, "GpioPk4" }, - { 0x09E8, 0xF0000000, "GpioPk5" }, - { 0x09EC, 0xF0000000, "GpioPk6" }, - { 0x09F0, 0xF0000000, "GpioPk7" }, - { 0x09F4, 0xF0000000, "GpioPl0" }, - { 0x09F8, 0xF0000000, "GpioPl1" }, - { 0x09FC, 0x07F7F000, "GpioPz0" }, - { 0x0A00, 0x07F7F000, "GpioPz1" }, - { 0x0A04, 0x07F7F000, "GpioPz2" }, - { 0x0A08, 0x07F7F000, "GpioPz3" }, - { 0x0A0C, 0x07F7F000, "GpioPz4" }, - { 0x0A10, 0x07F7F000, "GpioPz5" }, - { 0x0A14, 0x01F1F000, "GpioX1Aud" }, - { 0x0A18, 0x01F1F000, "GpioX3Aud" }, - { 0x0A1C, 0x01F1F000, "GpsEn" }, - { 0x0A20, 0x01F1F000, "GpsRst" }, - { 0x0A24, 0x01F1F000, "HdmiCec" }, - { 0x0A28, 0x01F1F000, "HdmiIntDpHpd" }, - { 0x0A2C, 0x01F1F000, "JtagRtck" }, - { 0x0A30, 0x01F1F000, "LcdBlEn" }, - { 0x0A34, 0x01F1F000, "LcdBlPwm" }, - { 0x0A38, 0x01F1F000, "LcdGpio1" }, - { 0x0A3C, 0x01F1F000, "LcdGpio2" }, - { 0x0A40, 0x01F1F000, "LcdRst" }, - { 0x0A44, 0x01F1F000, "LcdTe" }, - { 0x0A48, 0x01F1F000, "ModemWakeAp" }, - { 0x0A4C, 0x01F1F000, "MotionInt" }, - { 0x0A50, 0x01F1F000, "NfcEn" }, - { 0x0A54, 0x01F1F000, "NfcInt" }, - { 0x0A58, 0x01F1F000, "PexL0ClkReqN" }, - { 0x0A5C, 0x01F1F000, "PexL0RstN" }, - { 0x0A60, 0x01F1F000, "PexL1ClkreqN" }, - { 0x0A64, 0x01F1F000, "PexL1RstN" }, - { 0x0A68, 0x01F1F000, "PexWakeN" }, - { 0x0A6C, 0x01F1F000, "PwrI2cScl" }, - { 0x0A70, 0x01F1F000, "PwrI2cSda" }, - { 0x0A74, 0x01F1F000, "PwrIntN" }, - { 0x0A78, 0x07F7F000, "QspiComp" }, - { 0x0A90, 0xF0000000, "QspiSck" }, - { 0x0A94, 0x01F1F000, "SataLedActive" }, - { 0x0A98, 0xF7F7F000, "Sdmmc1Pad" }, - { 0x0AB0, 0xF7F7F000, "Sdmmc3Pad" }, - { 0x0AC8, 0x01F1F000, "Shutdown" }, - { 0x0ACC, 0x01F1F000, "SpdifIn" }, - { 0x0AD0, 0x01F1F000, "SpdifOut" }, - { 0x0AD4, 0xF0000000, "Spi1Cs0" }, - { 0x0AD8, 0xF0000000, "Spi1Cs1" }, - { 0x0ADC, 0xF0000000, "Spi1Miso" }, - { 0x0AE0, 0xF0000000, "Spi1Mosi" }, - { 0x0AE4, 0xF0000000, "Spi1Sck" }, - { 0x0AE8, 0xF0000000, "Spi2Cs0" }, - { 0x0AEC, 0xF0000000, "Spi2Cs1" }, - { 0x0AF0, 0xF0000000, "Spi2Miso" }, - { 0x0AF4, 0xF0000000, "Spi2Mosi" }, - { 0x0AF8, 0xF0000000, "Spi2Sck" }, - { 0x0AFC, 0xF0000000, "Spi4Cs0" }, - { 0x0B00, 0xF0000000, "Spi4Miso" }, - { 0x0B04, 0xF0000000, "Spi4Mosi" }, - { 0x0B08, 0xF0000000, "Spi4Sck" }, - { 0x0B0C, 0x01F1F000, "TempAlert" }, - { 0x0B10, 0x01F1F000, "TouchClk" }, - { 0x0B14, 0x01F1F000, "TouchInt" }, - { 0x0B18, 0x01F1F000, "TouchRst" }, - { 0x0B1C, 0x01F1F000, "Uart1Cts" }, - { 0x0B20, 0x01F1F000, "Uart1Rts" }, - { 0x0B24, 0x01F1F000, "Uart1Rx" }, - { 0x0B28, 0x01F1F000, "Uart1Tx" }, - { 0x0B2C, 0x01F1F000, "Uart2Cts" }, - { 0x0B30, 0x01F1F000, "Uart2Rts" }, - { 0x0B34, 0x01F1F000, "Uart2Rx" }, - { 0x0B38, 0x01F1F000, "Uart2Tx" }, - { 0x0B3C, 0x01F1F000, "Uart3Cts" }, - { 0x0B40, 0x01F1F000, "Uart3Rts" }, - { 0x0B44, 0x01F1F000, "Uart3Rx" }, - { 0x0B48, 0x01F1F000, "Uart3Tx" }, - { 0x0B4C, 0x01F1F000, "Uart4Cts" }, - { 0x0B50, 0x01F1F000, "Uart4Rts" }, - { 0x0B54, 0x01F1F000, "Uart4Rx" }, - { 0x0B58, 0x01F1F000, "Uart4Tx" }, - { 0x0B5C, 0x01F1F000, "UsbVbusEn0" }, - { 0x0B60, 0x01F1F000, "UsbVbusEn1" }, - { 0x0B64, 0x01F1F000, "WifiEn" }, - { 0x0B68, 0x01F1F000, "WifiRst" }, - { 0x0B6C, 0x01F1F000, "WifiWakeAp" }, -}; - -constexpr inline size_t NumPinmuxDrivePadCharacters = util::size(PinmuxDrivePadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc deleted file mode 100644 index 2caa615..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigs[] = { - { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen2I2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen2I2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, -}; - -constexpr inline const size_t NumPinmuxDrivePadConfigs = util::size(PinmuxDrivePadConfigs); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc deleted file mode 100644 index 8831b8f..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigsHoag[] = { - { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen2I2cScl, 0x00004000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen2I2cSda, 0x00004000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, - { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, - { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, - { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, -}; - -constexpr inline const size_t NumPinmuxDrivePadConfigsHoag = util::size(PinmuxDrivePadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_aula.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_aula.inc deleted file mode 100644 index 14bcc76..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_aula.inc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxPadConfig PinmuxPadConfigsAula[] = { -{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, -{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, -{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_WifiEn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, -}; - -constexpr inline const size_t NumPinmuxPadConfigsAula = util::size(PinmuxPadConfigsAula); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc deleted file mode 100644 index 94bca5c..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxPadConfig PinmuxPadConfigsCalcio[] = { -{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_HdmiCec, 0x00000240, 0x0000027F }, -{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPe6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_Gen3I2cSda, 0x00000204, 0x0000027F }, -{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, -{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_Spi1Mosi, 0x0000000C, 0x0000007F }, -{ PinmuxPadIndex_Spi1Miso, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_Spi1Sck, 0x0000000C, 0x0000007F }, -{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPh6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_DpHpd0, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000024, 0x0000027F }, -{ PinmuxPadIndex_AudMclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioX1Aud, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioX3Aud, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap1Din, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap1Dout, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap1Fs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap1Sclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Gen3I2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cSda, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam2Mclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamFlashEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SataLedActive, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_MotionInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ButtonSlideSw, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz4, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000018 }, -{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000018 }, -{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Sclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Gen2I2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Gen2I2cSda, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk5, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPl1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi4Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi4Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi4Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi4Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart3Tx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart3Rx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart3Rts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart3Cts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, -}; - -constexpr inline const size_t NumPinmuxPadConfigsCalcio = util::size(PinmuxPadConfigsCalcio); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc deleted file mode 100644 index d0efa99..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxPadConfig PinmuxPadConfigsHoag[] = { -{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_TouchClk, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, -{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart3Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPe7, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, -{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Cam2Pwdn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Cam1Strobe, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_LcdGpio1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Dout, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Fs, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, -{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk7, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Spi4Mosi, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Spi4Miso, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Spi4Sck, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Spi4Cs0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_UsbVbusEn1, 0x00000204, 0x00000267 }, -{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, -}; - -constexpr inline const size_t NumPinmuxPadConfigsHoag = util::size(PinmuxPadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc deleted file mode 100644 index e1cee6f..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxPadConfig PinmuxPadConfigsIcosa[] = { -{ PinmuxPadIndex_AudMclk, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x00000267 }, -{ PinmuxPadIndex_PexL1ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x00000267 }, -{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_Sdmmc1Clk, 0x00000048, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, -{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000060 }, -{ PinmuxPadIndex_PwrIntN, 0x00000030, 0x00000078 }, -{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_QspiIo0, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_QspiIo1, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_QspiSck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_QspiCsN, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dmic3Dat, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, -{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Cam1Pwdn, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonPowerOn, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonSlideSw, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPz0, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, -{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk7, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, -{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, -}; - -constexpr inline const size_t NumPinmuxPadConfigsIcosa = util::size(PinmuxPadConfigsIcosa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc deleted file mode 100644 index 3b0022e..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ - -constexpr inline const PinmuxPadConfig PinmuxPadConfigsIowa[] = { -{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, -{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, -{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, -{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, -{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, -{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, -{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, -{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, -{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, -{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, -{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, -{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, -{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, -{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, -{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, -{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, -{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, -{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, -{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, -{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, -{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, -{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, -{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, -{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, -{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, -}; - -constexpr inline const size_t NumPinmuxPadConfigsIowa = util::size(PinmuxPadConfigsIowa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc deleted file mode 100644 index a84081d..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ - -constexpr inline const PinmuxPadCharacter PinmuxPadCharacters[] = { - { 0x3000, 0x072FF, 0x01, "Sdmmc1Clk" }, - { 0x3004, 0x072FF, 0x02, "Sdmmc1Cmd" }, - { 0x3008, 0x072FF, 0x02, "Sdmmc1Dat3" }, - { 0x300C, 0x072FF, 0x02, "Sdmmc1Dat2" }, - { 0x3010, 0x072FF, 0x02, "Sdmmc1Dat1" }, - { 0x3014, 0x072FF, 0x01, "Sdmmc1Dat0" }, - { 0x301C, 0x072FF, 0x01, "Sdmmc3Clk" }, - { 0x3020, 0x072FF, 0x01, "Sdmmc3Cmd" }, - { 0x3024, 0x072FF, 0x01, "Sdmmc3Dat0" }, - { 0x3028, 0x072FF, 0x01, "Sdmmc3Dat1" }, - { 0x302C, 0x072FF, 0x01, "Sdmmc3Dat2" }, - { 0x3030, 0x072FF, 0x01, "Sdmmc3Dat3" }, - { 0x3038, 0x01DFF, 0x01, "PexL0RstN" }, - { 0x303C, 0x01DFF, 0x01, "PexL0ClkreqN" }, - { 0x3040, 0x01DFF, 0x01, "PexWakeN" }, - { 0x3044, 0x01DFF, 0x01, "PexL1RstN" }, - { 0x3048, 0x01DFF, 0x01, "PexL1ClkreqN" }, - { 0x304C, 0x019FF, 0x01, "SataLedActive" }, - { 0x3050, 0x1F2FF, 0x01, "Spi1Mosi" }, - { 0x3054, 0x1F2FF, 0x01, "Spi1Miso" }, - { 0x3058, 0x1F2FF, 0x01, "Spi1Sck" }, - { 0x305C, 0x1F2FF, 0x01, "Spi1Cs0" }, - { 0x3060, 0x1F2FF, 0x01, "Spi1Cs1" }, - { 0x3064, 0x072FF, 0x02, "Spi2Mosi" }, - { 0x3068, 0x072FF, 0x02, "Spi2Miso" }, - { 0x306C, 0x072FF, 0x02, "Spi2Sck" }, - { 0x3070, 0x072FF, 0x02, "Spi2Cs0" }, - { 0x3074, 0x072FF, 0x01, "Spi2Cs1" }, - { 0x3078, 0x1F2FF, 0x01, "Spi4Mosi" }, - { 0x307C, 0x1F2FF, 0x01, "Spi4Miso" }, - { 0x3080, 0x1F2FF, 0x01, "Spi4Sck" }, - { 0x3084, 0x1F2FF, 0x01, "Spi4Cs0" }, - { 0x3088, 0x072FF, 0x01, "QspiSck" }, - { 0x308C, 0x072FF, 0x01, "QspiCsN" }, - { 0x3090, 0x072FF, 0x01, "QspiIo0" }, - { 0x3094, 0x072FF, 0x01, "QspiIo1" }, - { 0x3098, 0x072FF, 0x01, "QspiIo2" }, - { 0x309C, 0x072FF, 0x01, "QspiIo3" }, - { 0x30A4, 0x019FF, 0x02, "Dmic1Clk" }, - { 0x30A8, 0x019FF, 0x02, "Dmic1Dat" }, - { 0x30AC, 0x019FF, 0x02, "Dmic2Clk" }, - { 0x30B0, 0x019FF, 0x02, "Dmic2Dat" }, - { 0x30B4, 0x019FF, 0x02, "Dmic3Clk" }, - { 0x30B8, 0x019FF, 0x02, "Dmic3Dat" }, - { 0x30BC, 0x01DFF, 0x01, "Gen1I2cScl" }, - { 0x30C0, 0x01DFF, 0x01, "Gen1I2cSda" }, - { 0x30C4, 0x01DFF, 0x01, "Gen2I2cScl" }, - { 0x30C8, 0x01DFF, 0x01, "Gen2I2cSda" }, - { 0x30CC, 0x01DFF, 0x01, "Gen3I2cScl" }, - { 0x30D0, 0x01DFF, 0x01, "Gen3I2cSda" }, - { 0x30D4, 0x01DFF, 0x02, "CamI2cScl" }, - { 0x30D8, 0x01DFF, 0x02, "CamI2cSda" }, - { 0x30DC, 0x01DFF, 0x01, "PwrI2cScl" }, - { 0x30E0, 0x01DFF, 0x01, "PwrI2cSda" }, - { 0x30E4, 0x019FF, 0x01, "Uart1Tx" }, - { 0x30E8, 0x019FF, 0x01, "Uart1Rx" }, - { 0x30EC, 0x019FF, 0x01, "Uart1Rts" }, - { 0x30F0, 0x019FF, 0x01, "Uart1Cts" }, - { 0x30F4, 0x019FF, 0x00, "Uart2Tx" }, - { 0x30F8, 0x019FF, 0x00, "Uart2Rx" }, - { 0x30FC, 0x019FF, 0x02, "Uart2Rts" }, - { 0x3100, 0x019FF, 0x02, "Uart2Cts" }, - { 0x3104, 0x019FF, 0x02, "Uart3Tx" }, - { 0x3108, 0x019FF, 0x02, "Uart3Rx" }, - { 0x310C, 0x019FF, 0x02, "Uart3Rts" }, - { 0x3110, 0x019FF, 0x02, "Uart3Cts" }, - { 0x3114, 0x019FF, 0x02, "Uart4Tx" }, - { 0x3118, 0x019FF, 0x02, "Uart4Rx" }, - { 0x311C, 0x019FF, 0x02, "Uart4Rts" }, - { 0x3120, 0x019FF, 0x02, "Uart4Cts" }, - { 0x3124, 0x072FF, 0x01, "Dap1Fs" }, - { 0x3128, 0x072FF, 0x01, "Dap1Din" }, - { 0x312C, 0x072FF, 0x01, "Dap1Dout" }, - { 0x3130, 0x072FF, 0x01, "Dap1Sclk" }, - { 0x3134, 0x072FF, 0x01, "Dap2Fs" }, - { 0x3138, 0x072FF, 0x01, "Dap2Din" }, - { 0x313C, 0x072FF, 0x01, "Dap2Dout" }, - { 0x3140, 0x072FF, 0x01, "Dap2Sclk" }, - { 0x3144, 0x072FF, 0x01, "Dap4Fs" }, - { 0x3148, 0x072FF, 0x01, "Dap4Din" }, - { 0x314C, 0x072FF, 0x01, "Dap4Dout" }, - { 0x3150, 0x072FF, 0x01, "Dap4Sclk" }, - { 0x3154, 0x072FF, 0x01, "Cam1Mclk" }, - { 0x3158, 0x072FF, 0x01, "Cam2Mclk" }, - { 0x315C, 0x072FF, 0x01, "JtagRtck" }, - { 0x3160, 0x0118C, 0xFF, "Clk32kIn" }, - { 0x3164, 0x072FF, 0x02, "Clk32kOut" }, - { 0x3168, 0x01DFF, 0x01, "BattBcl" }, - { 0x316C, 0x011CC, 0xFF, "ClkReq" }, - { 0x3170, 0x011CC, 0xFF, "CpuPwrReq" }, - { 0x3174, 0x011CC, 0xFF, "PwrIntN" }, - { 0x3178, 0x011CC, 0xFF, "Shutdown" }, - { 0x317C, 0x011CC, 0xFF, "CorePwrReq" }, - { 0x3180, 0x019FF, 0x01, "AudMclk" }, - { 0x3184, 0x019FF, 0x00, "DvfsPwm" }, - { 0x3188, 0x019FF, 0x00, "DvfsClk" }, - { 0x318C, 0x019FF, 0x00, "GpioX1Aud" }, - { 0x3190, 0x019FF, 0x00, "GpioX3Aud" }, - { 0x3194, 0x01DFF, 0x00, "GpioPcc7" }, - { 0x3198, 0x01DFF, 0x01, "HdmiCec" }, - { 0x319C, 0x01DFF, 0x01, "HdmiIntDpHpd" }, - { 0x31A0, 0x019FF, 0x01, "SpdifOut" }, - { 0x31A4, 0x019FF, 0x01, "SpdifIn" }, - { 0x31A8, 0x01DFF, 0x01, "UsbVbusEn0" }, - { 0x31AC, 0x01DFF, 0x01, "UsbVbusEn1" }, - { 0x31B0, 0x019FF, 0x01, "DpHpd0" }, - { 0x31B4, 0x019FF, 0x00, "WifiEn" }, - { 0x31B8, 0x019FF, 0x00, "WifiRst" }, - { 0x31BC, 0x019FF, 0x00, "WifiWakeAp" }, - { 0x31C0, 0x019FF, 0x00, "ApWakeBt" }, - { 0x31C4, 0x019FF, 0x00, "BtRst" }, - { 0x31C8, 0x019FF, 0x00, "BtWakeAp" }, - { 0x31CC, 0x019FF, 0x00, "ApWakeNfc" }, - { 0x31D0, 0x019FF, 0x00, "NfcEn" }, - { 0x31D4, 0x019FF, 0x00, "NfcInt" }, - { 0x31D8, 0x019FF, 0x00, "GpsEn" }, - { 0x31DC, 0x019FF, 0x00, "GpsRst" }, - { 0x31E0, 0x019FF, 0x01, "CamRst" }, - { 0x31E4, 0x019FF, 0x02, "CamAfEn" }, - { 0x31E8, 0x019FF, 0x02, "CamFlashEn" }, - { 0x31EC, 0x019FF, 0x01, "Cam1Pwdn" }, - { 0x31F0, 0x019FF, 0x01, "Cam2Pwdn" }, - { 0x31F4, 0x019FF, 0x01, "Cam1Strobe" }, - { 0x31F8, 0x019FF, 0x01, "LcdTe" }, - { 0x31FC, 0x019FF, 0x03, "LcdBlPwm" }, - { 0x3200, 0x019FF, 0x00, "LcdBlEn" }, - { 0x3204, 0x019FF, 0x00, "LcdRst" }, - { 0x3208, 0x019FF, 0x01, "LcdGpio1" }, - { 0x320C, 0x019FF, 0x02, "LcdGpio2" }, - { 0x3210, 0x019FF, 0x00, "ApReady" }, - { 0x3214, 0x019FF, 0x00, "TouchRst" }, - { 0x3218, 0x019FF, 0x01, "TouchClk" }, - { 0x321C, 0x019FF, 0x00, "ModemWakeAp" }, - { 0x3220, 0x019FF, 0x00, "TouchInt" }, - { 0x3224, 0x019FF, 0x00, "MotionInt" }, - { 0x3228, 0x019FF, 0x00, "AlsProxInt" }, - { 0x322C, 0x019FF, 0x00, "TempAlert" }, - { 0x3230, 0x019FF, 0x00, "ButtonPowerOn" }, - { 0x3234, 0x019FF, 0x00, "ButtonVolUp" }, - { 0x3238, 0x019FF, 0x00, "ButtonVolDown" }, - { 0x323C, 0x019FF, 0x00, "ButtonSlideSw" }, - { 0x3240, 0x019FF, 0x00, "ButtonHome" }, - { 0x3244, 0x019FF, 0x01, "GpioPa6" }, - { 0x3248, 0x019FF, 0x00, "GpioPe6" }, - { 0x324C, 0x019FF, 0x00, "GpioPe7" }, - { 0x3250, 0x019FF, 0x00, "GpioPh6" }, - { 0x3254, 0x072FF, 0x02, "GpioPk0" }, - { 0x3258, 0x072FF, 0x02, "GpioPk1" }, - { 0x325C, 0x072FF, 0x02, "GpioPk2" }, - { 0x3260, 0x072FF, 0x02, "GpioPk3" }, - { 0x3264, 0x072FF, 0x01, "GpioPk4" }, - { 0x3268, 0x072FF, 0x01, "GpioPk5" }, - { 0x326C, 0x072FF, 0x01, "GpioPk6" }, - { 0x3270, 0x072FF, 0x01, "GpioPk7" }, - { 0x3274, 0x072FF, 0x00, "GpioPl0" }, - { 0x3278, 0x072FF, 0x01, "GpioPl1" }, - { 0x327C, 0x072FF, 0x01, "GpioPz0" }, - { 0x3280, 0x072FF, 0x02, "GpioPz1" }, - { 0x3284, 0x072FF, 0x02, "GpioPz2" }, - { 0x3288, 0x072FF, 0x01, "GpioPz3" }, - { 0x328C, 0x072FF, 0x01, "GpioPz4" }, - { 0x3290, 0x072FF, 0x01, "GpioPz5" }, - { 0x3294, 0x1F2FF, 0x02, "Sdmmc2Dat0" }, - { 0x3298, 0x1F2FF, 0x02, "Sdmmc2Dat1" }, - { 0x329C, 0x1F2FF, 0x02, "Sdmmc2Dat2" }, - { 0x32A0, 0x1F2FF, 0x02, "Sdmmc2Dat3" }, - { 0x32A4, 0x1F2FF, 0x02, "Sdmmc2Dat4" }, - { 0x32A8, 0x1F2FF, 0x02, "Sdmmc2Dat5" }, - { 0x32AC, 0x1F2FF, 0x02, "Sdmmc2Dat6" }, - { 0x32B0, 0x1F2FF, 0x02, "Sdmmc2Dat7" }, - { 0x32B4, 0x1F2FF, 0x02, "Sdmmc2Clk" }, - { 0x32B8, 0x1F2FF, 0x00, "Sdmmc2Clkb" }, - { 0x32BC, 0x1F2FF, 0x02, "Sdmmc2Cmd" }, - { 0x32C0, 0x1F2FF, 0x00, "Sdmmc2Dqs" }, - { 0x32C4, 0x1F2FF, 0x00, "Sdmmc2Dqsb" }, -}; - -constexpr inline size_t NumPinmuxPadCharacters = util::size(PinmuxPadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp deleted file mode 100644 index df2dc59..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ - -#pragma once - -enum PinmuxPadIndex { - PinmuxPadIndex_Sdmmc1Clk = 0, - PinmuxPadIndex_Sdmmc1Cmd = 1, - PinmuxPadIndex_Sdmmc1Dat3 = 2, - PinmuxPadIndex_Sdmmc1Dat2 = 3, - PinmuxPadIndex_Sdmmc1Dat1 = 4, - PinmuxPadIndex_Sdmmc1Dat0 = 5, - PinmuxPadIndex_Sdmmc3Clk = 6, - PinmuxPadIndex_Sdmmc3Cmd = 7, - PinmuxPadIndex_Sdmmc3Dat0 = 8, - PinmuxPadIndex_Sdmmc3Dat1 = 9, - PinmuxPadIndex_Sdmmc3Dat2 = 10, - PinmuxPadIndex_Sdmmc3Dat3 = 11, - PinmuxPadIndex_PexL0RstN = 12, - PinmuxPadIndex_PexL0ClkreqN = 13, - PinmuxPadIndex_PexWakeN = 14, - PinmuxPadIndex_PexL1RstN = 15, - PinmuxPadIndex_PexL1ClkreqN = 16, - PinmuxPadIndex_SataLedActive = 17, - PinmuxPadIndex_Spi1Mosi = 18, - PinmuxPadIndex_Spi1Miso = 19, - PinmuxPadIndex_Spi1Sck = 20, - PinmuxPadIndex_Spi1Cs0 = 21, - PinmuxPadIndex_Spi1Cs1 = 22, - PinmuxPadIndex_Spi2Mosi = 23, - PinmuxPadIndex_Spi2Miso = 24, - PinmuxPadIndex_Spi2Sck = 25, - PinmuxPadIndex_Spi2Cs0 = 26, - PinmuxPadIndex_Spi2Cs1 = 27, - PinmuxPadIndex_Spi4Mosi = 28, - PinmuxPadIndex_Spi4Miso = 29, - PinmuxPadIndex_Spi4Sck = 30, - PinmuxPadIndex_Spi4Cs0 = 31, - PinmuxPadIndex_QspiSck = 32, - PinmuxPadIndex_QspiCsN = 33, - PinmuxPadIndex_QspiIo0 = 34, - PinmuxPadIndex_QspiIo1 = 35, - PinmuxPadIndex_QspiIo2 = 36, - PinmuxPadIndex_QspiIo3 = 37, - PinmuxPadIndex_Dmic1Clk = 38, - PinmuxPadIndex_Dmic1Dat = 39, - PinmuxPadIndex_Dmic2Clk = 40, - PinmuxPadIndex_Dmic2Dat = 41, - PinmuxPadIndex_Dmic3Clk = 42, - PinmuxPadIndex_Dmic3Dat = 43, - PinmuxPadIndex_Gen1I2cScl = 44, - PinmuxPadIndex_Gen1I2cSda = 45, - PinmuxPadIndex_Gen2I2cScl = 46, - PinmuxPadIndex_Gen2I2cSda = 47, - PinmuxPadIndex_Gen3I2cScl = 48, - PinmuxPadIndex_Gen3I2cSda = 49, - PinmuxPadIndex_CamI2cScl = 50, - PinmuxPadIndex_CamI2cSda = 51, - PinmuxPadIndex_PwrI2cScl = 52, - PinmuxPadIndex_PwrI2cSda = 53, - PinmuxPadIndex_Uart1Tx = 54, - PinmuxPadIndex_Uart1Rx = 55, - PinmuxPadIndex_Uart1Rts = 56, - PinmuxPadIndex_Uart1Cts = 57, - PinmuxPadIndex_Uart2Tx = 58, - PinmuxPadIndex_Uart2Rx = 59, - PinmuxPadIndex_Uart2Rts = 60, - PinmuxPadIndex_Uart2Cts = 61, - PinmuxPadIndex_Uart3Tx = 62, - PinmuxPadIndex_Uart3Rx = 63, - PinmuxPadIndex_Uart3Rts = 64, - PinmuxPadIndex_Uart3Cts = 65, - PinmuxPadIndex_Uart4Tx = 66, - PinmuxPadIndex_Uart4Rx = 67, - PinmuxPadIndex_Uart4Rts = 68, - PinmuxPadIndex_Uart4Cts = 69, - PinmuxPadIndex_Dap1Fs = 70, - PinmuxPadIndex_Dap1Din = 71, - PinmuxPadIndex_Dap1Dout = 72, - PinmuxPadIndex_Dap1Sclk = 73, - PinmuxPadIndex_Dap2Fs = 74, - PinmuxPadIndex_Dap2Din = 75, - PinmuxPadIndex_Dap2Dout = 76, - PinmuxPadIndex_Dap2Sclk = 77, - PinmuxPadIndex_Dap4Fs = 78, - PinmuxPadIndex_Dap4Din = 79, - PinmuxPadIndex_Dap4Dout = 80, - PinmuxPadIndex_Dap4Sclk = 81, - PinmuxPadIndex_Cam1Mclk = 82, - PinmuxPadIndex_Cam2Mclk = 83, - PinmuxPadIndex_JtagRtck = 84, - PinmuxPadIndex_Clk32kIn = 85, - PinmuxPadIndex_Clk32kOut = 86, - PinmuxPadIndex_BattBcl = 87, - PinmuxPadIndex_ClkReq = 88, - PinmuxPadIndex_CpuPwrReq = 89, - PinmuxPadIndex_PwrIntN = 90, - PinmuxPadIndex_Shutdown = 91, - PinmuxPadIndex_CorePwrReq = 92, - PinmuxPadIndex_AudMclk = 93, - PinmuxPadIndex_DvfsPwm = 94, - PinmuxPadIndex_DvfsClk = 95, - PinmuxPadIndex_GpioX1Aud = 96, - PinmuxPadIndex_GpioX3Aud = 97, - PinmuxPadIndex_GpioPcc7 = 98, - PinmuxPadIndex_HdmiCec = 99, - PinmuxPadIndex_HdmiIntDpHpd = 100, - PinmuxPadIndex_SpdifOut = 101, - PinmuxPadIndex_SpdifIn = 102, - PinmuxPadIndex_UsbVbusEn0 = 103, - PinmuxPadIndex_UsbVbusEn1 = 104, - PinmuxPadIndex_DpHpd0 = 105, - PinmuxPadIndex_WifiEn = 106, - PinmuxPadIndex_WifiRst = 107, - PinmuxPadIndex_WifiWakeAp = 108, - PinmuxPadIndex_ApWakeBt = 109, - PinmuxPadIndex_BtRst = 110, - PinmuxPadIndex_BtWakeAp = 111, - PinmuxPadIndex_ApWakeNfc = 112, - PinmuxPadIndex_NfcEn = 113, - PinmuxPadIndex_NfcInt = 114, - PinmuxPadIndex_GpsEn = 115, - PinmuxPadIndex_GpsRst = 116, - PinmuxPadIndex_CamRst = 117, - PinmuxPadIndex_CamAfEn = 118, - PinmuxPadIndex_CamFlashEn = 119, - PinmuxPadIndex_Cam1Pwdn = 120, - PinmuxPadIndex_Cam2Pwdn = 121, - PinmuxPadIndex_Cam1Strobe = 122, - PinmuxPadIndex_LcdTe = 123, - PinmuxPadIndex_LcdBlPwm = 124, - PinmuxPadIndex_LcdBlEn = 125, - PinmuxPadIndex_LcdRst = 126, - PinmuxPadIndex_LcdGpio1 = 127, - PinmuxPadIndex_LcdGpio2 = 128, - PinmuxPadIndex_ApReady = 129, - PinmuxPadIndex_TouchRst = 130, - PinmuxPadIndex_TouchClk = 131, - PinmuxPadIndex_ModemWakeAp = 132, - PinmuxPadIndex_TouchInt = 133, - PinmuxPadIndex_MotionInt = 134, - PinmuxPadIndex_AlsProxInt = 135, - PinmuxPadIndex_TempAlert = 136, - PinmuxPadIndex_ButtonPowerOn = 137, - PinmuxPadIndex_ButtonVolUp = 138, - PinmuxPadIndex_ButtonVolDown = 139, - PinmuxPadIndex_ButtonSlideSw = 140, - PinmuxPadIndex_ButtonHome = 141, - PinmuxPadIndex_GpioPa6 = 142, - PinmuxPadIndex_GpioPe6 = 143, - PinmuxPadIndex_GpioPe7 = 144, - PinmuxPadIndex_GpioPh6 = 145, - PinmuxPadIndex_GpioPk0 = 146, - PinmuxPadIndex_GpioPk1 = 147, - PinmuxPadIndex_GpioPk2 = 148, - PinmuxPadIndex_GpioPk3 = 149, - PinmuxPadIndex_GpioPk4 = 150, - PinmuxPadIndex_GpioPk5 = 151, - PinmuxPadIndex_GpioPk6 = 152, - PinmuxPadIndex_GpioPk7 = 153, - PinmuxPadIndex_GpioPl0 = 154, - PinmuxPadIndex_GpioPl1 = 155, - PinmuxPadIndex_GpioPz0 = 156, - PinmuxPadIndex_GpioPz1 = 157, - PinmuxPadIndex_GpioPz2 = 158, - PinmuxPadIndex_GpioPz3 = 159, - PinmuxPadIndex_GpioPz4 = 160, - PinmuxPadIndex_GpioPz5 = 161, - PinmuxPadIndex_Sdmmc2Dat0 = 162, - PinmuxPadIndex_Sdmmc2Dat1 = 163, - PinmuxPadIndex_Sdmmc2Dat2 = 164, - PinmuxPadIndex_Sdmmc2Dat3 = 165, - PinmuxPadIndex_Sdmmc2Dat4 = 166, - PinmuxPadIndex_Sdmmc2Dat5 = 167, - PinmuxPadIndex_Sdmmc2Dat6 = 168, - PinmuxPadIndex_Sdmmc2Dat7 = 169, - PinmuxPadIndex_Sdmmc2Clk = 170, - PinmuxPadIndex_Sdmmc2Clkb = 171, - PinmuxPadIndex_Sdmmc2Cmd = 172, - PinmuxPadIndex_Sdmmc2Dqs = 173, - PinmuxPadIndex_Sdmmc2Dqsb = 174, -}; - -enum PinmuxDrivePadIndex { - PinmuxDrivePadIndex_AlsProxInt = 0, - PinmuxDrivePadIndex_ApReady = 1, - PinmuxDrivePadIndex_ApWakeBt = 2, - PinmuxDrivePadIndex_ApWakeNfc = 3, - PinmuxDrivePadIndex_AudMclk = 4, - PinmuxDrivePadIndex_BattBcl = 5, - PinmuxDrivePadIndex_BtRst = 6, - PinmuxDrivePadIndex_BtWakeAp = 7, - PinmuxDrivePadIndex_ButtonHome = 8, - PinmuxDrivePadIndex_ButtonPowerOn = 9, - PinmuxDrivePadIndex_ButtonSlideSw = 10, - PinmuxDrivePadIndex_ButtonVolDown = 11, - PinmuxDrivePadIndex_ButtonVolUp = 12, - PinmuxDrivePadIndex_Cam1Mclk = 13, - PinmuxDrivePadIndex_Cam1Pwdn = 14, - PinmuxDrivePadIndex_Cam1Strobe = 15, - PinmuxDrivePadIndex_Cam2Mclk = 16, - PinmuxDrivePadIndex_Cam2Pwdn = 17, - PinmuxDrivePadIndex_CamAfEn = 18, - PinmuxDrivePadIndex_CamFlashEn = 19, - PinmuxDrivePadIndex_CamI2cScl = 20, - PinmuxDrivePadIndex_CamI2cSda = 21, - PinmuxDrivePadIndex_CamRst = 22, - PinmuxDrivePadIndex_Clk32kIn = 23, - PinmuxDrivePadIndex_Clk32kOut = 24, - PinmuxDrivePadIndex_ClkReq = 25, - PinmuxDrivePadIndex_CorePwrReq = 26, - PinmuxDrivePadIndex_CpuPwrReq = 27, - PinmuxDrivePadIndex_Dap1Din = 28, - PinmuxDrivePadIndex_Dap1Dout = 29, - PinmuxDrivePadIndex_Dap1Fs = 30, - PinmuxDrivePadIndex_Dap1Sclk = 31, - PinmuxDrivePadIndex_Dap2Din = 32, - PinmuxDrivePadIndex_Dap2Dout = 33, - PinmuxDrivePadIndex_Dap2Fs = 34, - PinmuxDrivePadIndex_Dap2Sclk = 35, - PinmuxDrivePadIndex_Dap4Din = 36, - PinmuxDrivePadIndex_Dap4Dout = 37, - PinmuxDrivePadIndex_Dap4Fs = 38, - PinmuxDrivePadIndex_Dap4Sclk = 39, - PinmuxDrivePadIndex_Dmic1Clk = 40, - PinmuxDrivePadIndex_Dmic1Dat = 41, - PinmuxDrivePadIndex_Dmic2Clk = 42, - PinmuxDrivePadIndex_Dmic2Dat = 43, - PinmuxDrivePadIndex_Dmic3Clk = 44, - PinmuxDrivePadIndex_Dmic3Dat = 45, - PinmuxDrivePadIndex_DpHpd = 46, - PinmuxDrivePadIndex_DvfsClk = 47, - PinmuxDrivePadIndex_DvfsPwm = 48, - PinmuxDrivePadIndex_Gen1I2cScl = 49, - PinmuxDrivePadIndex_Gen1I2cSda = 50, - PinmuxDrivePadIndex_Gen2I2cScl = 51, - PinmuxDrivePadIndex_Gen2I2cSda = 52, - PinmuxDrivePadIndex_Gen3I2cScl = 53, - PinmuxDrivePadIndex_Gen3I2cSda = 54, - PinmuxDrivePadIndex_GpioPa6 = 55, - PinmuxDrivePadIndex_GpioPcc7 = 56, - PinmuxDrivePadIndex_GpioPe6 = 57, - PinmuxDrivePadIndex_GpioPe7 = 58, - PinmuxDrivePadIndex_GpioPh6 = 59, - PinmuxDrivePadIndex_GpioPk0 = 60, - PinmuxDrivePadIndex_GpioPk1 = 61, - PinmuxDrivePadIndex_GpioPk2 = 62, - PinmuxDrivePadIndex_GpioPk3 = 63, - PinmuxDrivePadIndex_GpioPk4 = 64, - PinmuxDrivePadIndex_GpioPk5 = 65, - PinmuxDrivePadIndex_GpioPk6 = 66, - PinmuxDrivePadIndex_GpioPk7 = 67, - PinmuxDrivePadIndex_GpioPl0 = 68, - PinmuxDrivePadIndex_GpioPl1 = 69, - PinmuxDrivePadIndex_GpioPz0 = 70, - PinmuxDrivePadIndex_GpioPz1 = 71, - PinmuxDrivePadIndex_GpioPz2 = 72, - PinmuxDrivePadIndex_GpioPz3 = 73, - PinmuxDrivePadIndex_GpioPz4 = 74, - PinmuxDrivePadIndex_GpioPz5 = 75, - PinmuxDrivePadIndex_GpioX1Aud = 76, - PinmuxDrivePadIndex_GpioX3Aud = 77, - PinmuxDrivePadIndex_GpsEn = 78, - PinmuxDrivePadIndex_GpsRst = 79, - PinmuxDrivePadIndex_HdmiCec = 80, - PinmuxDrivePadIndex_HdmiIntDpHpd = 81, - PinmuxDrivePadIndex_JtagRtck = 82, - PinmuxDrivePadIndex_LcdBlEn = 83, - PinmuxDrivePadIndex_LcdBlPwm = 84, - PinmuxDrivePadIndex_LcdGpio1 = 85, - PinmuxDrivePadIndex_LcdGpio2 = 86, - PinmuxDrivePadIndex_LcdRst = 87, - PinmuxDrivePadIndex_LcdTe = 88, - PinmuxDrivePadIndex_ModemWakeAp = 89, - PinmuxDrivePadIndex_MotionInt = 90, - PinmuxDrivePadIndex_NfcEn = 91, - PinmuxDrivePadIndex_NfcInt = 92, - PinmuxDrivePadIndex_PexL0ClkReqN = 93, - PinmuxDrivePadIndex_PexL0RstN = 94, - PinmuxDrivePadIndex_PexL1ClkreqN = 95, - PinmuxDrivePadIndex_PexL1RstN = 96, - PinmuxDrivePadIndex_PexWakeN = 97, - PinmuxDrivePadIndex_PwrI2cScl = 98, - PinmuxDrivePadIndex_PwrI2cSda = 99, - PinmuxDrivePadIndex_PwrIntN = 100, - PinmuxDrivePadIndex_QspiComp = 101, - PinmuxDrivePadIndex_QspiSck = 102, - PinmuxDrivePadIndex_SataLedActive = 103, - PinmuxDrivePadIndex_Sdmmc1Pad = 104, - PinmuxDrivePadIndex_Sdmmc3Pad = 105, - PinmuxDrivePadIndex_Shutdown = 106, - PinmuxDrivePadIndex_SpdifIn = 107, - PinmuxDrivePadIndex_SpdifOut = 108, - PinmuxDrivePadIndex_Spi1Cs0 = 109, - PinmuxDrivePadIndex_Spi1Cs1 = 110, - PinmuxDrivePadIndex_Spi1Miso = 111, - PinmuxDrivePadIndex_Spi1Mosi = 112, - PinmuxDrivePadIndex_Spi1Sck = 113, - PinmuxDrivePadIndex_Spi2Cs0 = 114, - PinmuxDrivePadIndex_Spi2Cs1 = 115, - PinmuxDrivePadIndex_Spi2Miso = 116, - PinmuxDrivePadIndex_Spi2Mosi = 117, - PinmuxDrivePadIndex_Spi2Sck = 118, - PinmuxDrivePadIndex_Spi4Cs0 = 119, - PinmuxDrivePadIndex_Spi4Miso = 120, - PinmuxDrivePadIndex_Spi4Mosi = 121, - PinmuxDrivePadIndex_Spi4Sck = 122, - PinmuxDrivePadIndex_TempAlert = 123, - PinmuxDrivePadIndex_TouchClk = 124, - PinmuxDrivePadIndex_TouchInt = 125, - PinmuxDrivePadIndex_TouchRst = 126, - PinmuxDrivePadIndex_Uart1Cts = 127, - PinmuxDrivePadIndex_Uart1Rts = 128, - PinmuxDrivePadIndex_Uart1Rx = 129, - PinmuxDrivePadIndex_Uart1Tx = 130, - PinmuxDrivePadIndex_Uart2Cts = 131, - PinmuxDrivePadIndex_Uart2Rts = 132, - PinmuxDrivePadIndex_Uart2Rx = 133, - PinmuxDrivePadIndex_Uart2Tx = 134, - PinmuxDrivePadIndex_Uart3Cts = 135, - PinmuxDrivePadIndex_Uart3Rts = 136, - PinmuxDrivePadIndex_Uart3Rx = 137, - PinmuxDrivePadIndex_Uart3Tx = 138, - PinmuxDrivePadIndex_Uart4Cts = 139, - PinmuxDrivePadIndex_Uart4Rts = 140, - PinmuxDrivePadIndex_Uart4Rx = 141, - PinmuxDrivePadIndex_Uart4Tx = 142, - PinmuxDrivePadIndex_UsbVbusEn0 = 143, - PinmuxDrivePadIndex_UsbVbusEn1 = 144, - PinmuxDrivePadIndex_WifiEn = 145, - PinmuxDrivePadIndex_WifiRst = 146, - PinmuxDrivePadIndex_WifiWakeAp = 147, -}; diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp deleted file mode 100644 index 7efce9d..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - * 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 "pinmux_pad_index.hpp" -#include "pinmux_board_driver_api.hpp" -#include "pinmux_platform_pads.hpp" - -namespace ams::pinmux::driver::board::nintendo_nx { - - namespace { - - uintptr_t g_apb_misc_virtual_address = dd::QueryIoMapping(0x70000000, 0x4000); - - enum PinmuxPadMask : u32 { - PinmuxPadMask_Pm = 0x3, - PinmuxPadMask_Pupd = 0xC, - PinmuxPadMask_Tristate = 0x10, - PinmuxPadMask_Park = 0x20, - PinmuxPadMask_EInput = 0x40, - PinmuxPadMask_Lock = 0x80, - PinmuxPadMask_ELpdr = 0x100, - PinmuxPadMask_EHsm = 0x200, - PinmuxPadMask_EIoHv = 0x400, - PinmuxPadMask_EOd = 0x800, - PinmuxPadMask_ESchmt = 0x1000, - PinmuxPadMask_DrvType = 0x6000, - PinmuxPadMask_Preemp = 0x8000, - PinmuxPadMask_IoReset = 0x10000, - }; - - enum PinmuxPadBitOffset : u32 { - PinmuxPadBitOffset_Pm = 0x0, - PinmuxPadBitOffset_Pupd = 0x2, - PinmuxPadBitOffset_Tristate = 0x4, - PinmuxPadBitOffset_Park = 0x5, - PinmuxPadBitOffset_EInput = 0x6, - PinmuxPadBitOffset_Lock = 0x7, - PinmuxPadBitOffset_ELpdr = 0x8, - PinmuxPadBitOffset_EHsm = 0x9, - PinmuxPadBitOffset_EIoHv = 0xA, - PinmuxPadBitOffset_EOd = 0xB, - PinmuxPadBitOffset_ESchmt = 0xC, - PinmuxPadBitOffset_DrvType = 0xD, - PinmuxPadBitOffset_Preemp = 0xF, - PinmuxPadBitOffset_IoReset = 0x10, - }; - - enum PinmuxOptBitMask : u32 { - PinmuxOptBitMask_Pm = 0x7, - PinmuxOptBitMask_Pupd = 0x18, - PinmuxOptBitMask_Dir = 0x60, - PinmuxOptBitMask_Lock = 0x80, - PinmuxOptBitMask_IoReset = 0x100, - PinmuxOptBitMask_IoHv = 0x200, - PinmuxOptBitMask_Park = 0x400, - PinmuxOptBitMask_Lpdr = 0x800, - PinmuxOptBitMask_Hsm = 0x1000, - PinmuxOptBitMask_Schmt = 0x2000, - PinmuxOptBitMask_DrvType = 0xC000, - PinmuxOptBitMask_Preemp = 0x10000, - }; - - enum PinmuxOptBitOffset { - PinmuxOptBitOffset_Pm = 0x0, - PinmuxOptBitOffset_Pupd = 0x3, - PinmuxOptBitOffset_Dir = 0x5, - PinmuxOptBitOffset_Lock = 0x7, - PinmuxOptBitOffset_IoReset = 0x8, - PinmuxOptBitOffset_IoHv = 0x9, - PinmuxOptBitOffset_Park = 0xA, - PinmuxOptBitOffset_Lpdr = 0xB, - PinmuxOptBitOffset_Hsm = 0xC, - PinmuxOptBitOffset_Schmt = 0xD, - PinmuxOptBitOffset_DrvType = 0xE, - PinmuxOptBitOffset_Preemp = 0x10, - }; - - enum PinmuxDrivePadMask : u32{ - PinmuxDrivePadMask_DrvDn = 0x0001F000, - PinmuxDrivePadMask_DrvUp = 0x01F00000, - PinmuxDrivePadMask_CzDrvDn = 0x0007F000, - PinmuxDrivePadMask_CzDrvUp = 0x07F00000, - PinmuxDrivePadMask_SlwR = 0x30000000, - PinmuxDrivePadMask_SlwF = 0xC0000000, - }; - - enum PinmuxDrivePadBitOffset : u32 { - PinmuxDrivePadBitOffset_DrvDn = 12, - PinmuxDrivePadBitOffset_DrvUp = 20, - PinmuxDrivePadBitOffset_CzDrvDn = 12, - PinmuxDrivePadBitOffset_CzDrvUp = 20, - PinmuxDrivePadBitOffset_SlwR = 28, - PinmuxDrivePadBitOffset_SlwF = 30, - }; - - enum PinmuxDriveOptBitMask : u32 { - PinmuxDriveOptBitMask_DrvDn = 0x0001F000, - PinmuxDriveOptBitMask_DrvUp = 0x01F00000, - PinmuxDriveOptBitMask_CzDrvDn = 0x0007F000, - PinmuxDriveOptBitMask_CzDrvUp = 0x07F00000, - PinmuxDriveOptBitMask_SlwR = 0x30000000, - PinmuxDriveOptBitMask_SlwF = 0xC0000000, - }; - - enum PinmuxDriveOptBitOffset : u32 { - PinmuxDriveOptBitOffset_DrvDn = 12, - PinmuxDriveOptBitOffset_DrvUp = 20, - PinmuxDriveOptBitOffset_CzDrvDn = 12, - PinmuxDriveOptBitOffset_CzDrvUp = 20, - PinmuxDriveOptBitOffset_SlwR = 28, - PinmuxDriveOptBitOffset_SlwF = 30, - }; - - enum PinmuxOpt : u32 { - /* Pm */ - PinmuxOpt_Gpio = 0x4, - PinmuxOpt_Unused = 0x5, - - /* Pupd */ - PinmuxOpt_NoPupd = 0x0, - PinmuxOpt_PullDown = 0x8, - PinmuxOpt_PullUp = 0x10, - - /* Dir */ - PinmuxOpt_Output = 0x0, - PinmuxOpt_Input = 0x20, - PinmuxOpt_Bidirection = 0x40, - PinmuxOpt_OpenDrain = 0x60, - - /* Lock */ - PinmuxOpt_Unlock = 0x0, - PinmuxOpt_Lock = 0x80, - - /* IoReset */ - PinmuxOpt_DisableIoReset = 0x0, - PinmuxOpt_EnableIoReset = 0x100, - - /* IoHv */ - PinmuxOpt_NormalVoltage = 0x0, - PinmuxOpt_HighVoltage = 0x200, - - /* Park */ - PinmuxOpt_ResetOnLowPower = 0x0, - PinmuxOpt_ParkOnLowPower = 0x400, - - /* Lpdr */ - PinmuxOpt_DisableBaseDriver = 0x0, - PinmuxOpt_EnableBaseDriver = 0x800, - - /* Hsm */ - PinmuxOpt_DisableHighSpeedMode = 0x0, - PinmuxOpt_EnableHighSpeedMode = 0x1000, - - /* Schmt */ - PinmuxOpt_CmosMode = 0x0, - PinmuxOpt_SchmittTrigger = 0x2000, - - /* DrvType */ - PinmuxOpt_DrvType1X = 0x0, - PinmuxOpt_DrvType2X = 0x4000, - PinmuxOpt_DrvType3X = 0x8000, - PinmuxOpt_DrvType4X = 0xC000, - - /* Preemp */ - PinmuxOpt_DisablePreemp = 0x0, - PinmuxOpt_EnablePreemp = 0x10000, - }; - - enum PinmuxPadPm : u32 { - PinmuxPadPm_Default = 0xFFFFFFFF, - PinmuxPadPm_Pm0 = 0x0, - PinmuxPadPm_Pm1 = 0x1, - PinmuxPadPm_Pm2 = 0x2, - PinmuxPadPm_Pm3 = 0x3, - PinmuxPadPm_Safe = 0x4, - }; - - struct PinmuxPadCharacter { - u32 reg_offset; - u32 reg_mask; - u8 safe_func; - const char *pad_name; - }; - - struct PinmuxDrivePadCharacter { - u32 reg_offset; - u32 reg_mask; - const char *pad_name; - }; - - #include "pinmux_pad_characters.inc" - #include "pinmux_drive_pad_characters.inc" - - class PinmuxPad { - private: - u32 reg_address; - u32 reg_mask; - u32 reg_value; - u8 safe_func; - const char *pad_name; - private: - bool IsValidRegisterAddress() const { - return this->reg_address - 0x70003000 <= 0x2C4; - } - - uintptr_t GetRegisterAddress() const { - return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); - } - - bool UpdateBits(u32 value, u32 offset, u32 mask) { - if ((this->reg_mask & mask) != 0) { - if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { - this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); - } - return true; - } else { - return false; - } - } - - u32 ReadReg() const { - if (this->IsValidRegisterAddress()) { - return reg::Read(this->GetRegisterAddress()); - } else { - return 0; - } - } - - void WriteReg() const { - if (this->IsValidRegisterAddress()) { - reg::Write(this->GetRegisterAddress(), this->reg_value); - } - } - - bool IsLocked() const { - return (this->reg_value & PinmuxPadMask_Lock) != 0; - } - - void UpdatePm(u8 v) { - if (v != 0xFF) { - if (v == PinmuxPadPm_Safe) { - v = this->safe_func; - } - this->UpdateBits(v, PinmuxPadBitOffset_Pm, PinmuxPadMask_Pm); - } - } - - void UpdatePupd(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_Pupd, PinmuxPadMask_Pupd); - } - } - - void UpdateTristate(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_Tristate, PinmuxPadMask_Tristate); - } - } - - void UpdateEInput(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_EInput, PinmuxPadMask_EInput); - } - } - - void UpdateEOd(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_EOd, PinmuxPadMask_EOd); - } - } - - void UpdateLock(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_Lock, PinmuxPadMask_Lock); - } - } - - void UpdateIoReset(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_IoReset, PinmuxPadMask_IoReset); - } - } - - void UpdatePark(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_Park, PinmuxPadMask_Park); - } - } - - void UpdateELpdr(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_ELpdr, PinmuxPadMask_ELpdr); - } - } - - void UpdateEHsm(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_EHsm, PinmuxPadMask_EHsm); - } - } - - void UpdateEIoHv(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_EIoHv, PinmuxPadMask_EIoHv); - } - } - - void UpdateESchmt(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_ESchmt, PinmuxPadMask_ESchmt); - } - } - - void UpdatePreemp(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_Preemp, PinmuxPadMask_Preemp); - } - } - - void UpdateDrvType(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxPadBitOffset_DrvType, PinmuxPadMask_DrvType); - } - } - public: - constexpr PinmuxPad() : reg_address(), reg_mask(), reg_value(), safe_func(), pad_name() { /* ... */ } - - void UpdatePinmuxPad(u32 config, u32 config_mask) { - /* Update register value. */ - this->reg_value = this->ReadReg(); - - /* Check if we're locked. */ - if (this->IsLocked()) { - return; - } - - /* Update PM. */ - if ((config_mask & PinmuxOptBitMask_Pm) != 0) { - const auto opt = (config & PinmuxOptBitMask_Pm); - u8 pm = PinmuxPadPm_Safe; - if (opt != PinmuxOpt_Gpio) { - if (opt == PinmuxOpt_Unused) { - this->UpdatePupd(true); - this->UpdateTristate(true); - this->UpdateEInput(0); - } else if (opt <= PinmuxOpt_Unused) { - pm = opt >> PinmuxOptBitOffset_Pm; - } - } - this->UpdatePm(pm); - } - - /* Update pupd. */ - if ((config_mask & PinmuxOptBitMask_Pupd) != 0) { - const auto opt = (config & PinmuxOptBitMask_Pupd); - if (opt == PinmuxOpt_NoPupd || opt == PinmuxOpt_PullDown || opt == PinmuxOpt_PullUp) { - this->UpdatePupd(opt >> PinmuxOptBitOffset_Pupd); - } - } - - /* Update direction. */ - if ((config_mask & PinmuxOptBitMask_Dir) != 0) { - const auto opt = (config & PinmuxOptBitMask_Dir); - if (opt == PinmuxOpt_Output) { - this->UpdateTristate(false); - this->UpdateEInput(false); - if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { - this->UpdateEOd(false); - } - } else if (opt == PinmuxOpt_Input) { - this->UpdateTristate(true); - this->UpdateEInput(true); - if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { - this->UpdateEOd(false); - } - } else if (opt == PinmuxOpt_Bidirection) { - this->UpdateTristate(false); - this->UpdateEInput(true); - if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { - this->UpdateEOd(false); - } - } else if (opt == PinmuxOpt_OpenDrain) { - this->UpdateTristate(false); - this->UpdateEInput(true); - this->UpdateEOd(true); - } - } - - /* Update Lock. */ - if ((config_mask & PinmuxOptBitMask_Lock) != 0) { - const auto opt = (config & PinmuxOptBitMask_Lock); - this->UpdateLock(opt != 0); - } - - /* Update IoReset. */ - if ((config_mask & PinmuxOptBitMask_IoReset) != 0) { - const auto opt = (config & PinmuxOptBitMask_IoReset); - this->UpdateIoReset(opt != 0); - } - - /* Update Park. */ - if ((config_mask & PinmuxOptBitMask_Park) != 0) { - const auto opt = (config & PinmuxOptBitMask_Park); - this->UpdatePark(opt != 0); - } - - /* Update Lpdr. */ - if ((config_mask & PinmuxOptBitMask_Lpdr) != 0) { - const auto opt = (config & PinmuxOptBitMask_Lpdr); - this->UpdateELpdr(opt != 0); - } - - /* Update Hsm. */ - if ((config_mask & PinmuxOptBitMask_Hsm) != 0) { - const auto opt = (config & PinmuxOptBitMask_Hsm); - this->UpdateEHsm(opt != 0); - } - - /* Update IoHv. */ - if ((config_mask & PinmuxOptBitMask_IoHv) != 0) { - const auto opt = (config & PinmuxOptBitMask_IoHv); - this->UpdateEIoHv(opt != 0); - } - - /* Update Schmt. */ - if ((config_mask & PinmuxOptBitMask_Schmt) != 0) { - const auto opt = (config & PinmuxOptBitMask_Schmt); - this->UpdateESchmt(opt != 0); - } - - /* Update Preemp. */ - if ((config_mask & PinmuxOptBitMask_Preemp) != 0) { - const auto opt = (config & PinmuxOptBitMask_Preemp); - this->UpdatePreemp(opt != 0); - } - - /* Update drive type. */ - if ((config_mask & PinmuxOptBitMask_DrvType) != 0) { - const auto opt = (config & PinmuxOptBitMask_DrvType); - this->UpdateDrvType(opt >> PinmuxOptBitOffset_DrvType); - } - - /* Write the updated register value. */ - this->WriteReg(); - } - - void SetCharacter(const PinmuxPadCharacter &character) { - this->reg_address = character.reg_offset + 0x70000000; - this->reg_mask = character.reg_mask; - this->safe_func = character.safe_func; - this->reg_value = this->ReadReg(); - this->pad_name = character.pad_name; - - if ((this->reg_mask & this->reg_value & PinmuxPadMask_Park) != 0) { - this->reg_value &= ~(PinmuxPadMask_Park); - } - - this->WriteReg(); - } - }; - - class PinmuxDrivePad { - private: - u32 reg_address; - u32 reg_mask; - u32 reg_value; - u8 safe_func; - const char *pad_name; - private: - bool IsValidRegisterAddress() const { - return this->reg_address - 0x700008E4 <= 0x288; - } - - uintptr_t GetRegisterAddress() const { - return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); - } - - bool UpdateBits(u32 value, u32 offset, u32 mask) { - if ((this->reg_mask & mask) != 0) { - if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { - this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); - } - return true; - } else { - return false; - } - } - - u32 ReadReg() const { - if (this->IsValidRegisterAddress()) { - return reg::Read(this->GetRegisterAddress()); - } else { - return 0; - } - } - - void WriteReg() const { - if (this->IsValidRegisterAddress()) { - reg::Write(this->GetRegisterAddress(), this->reg_value); - } - } - - bool IsCzDrvDn() const { - return (this->reg_mask & PinmuxDrivePadMask_CzDrvDn) == PinmuxDrivePadMask_CzDrvDn; - } - - bool IsCzDrvUp() const { - return (this->reg_mask & PinmuxDrivePadMask_CzDrvUp) == PinmuxDrivePadMask_CzDrvUp; - } - - void UpdateDrvDn(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvDn, PinmuxDrivePadMask_DrvDn); - } - } - - void UpdateDrvUp(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvUp, PinmuxDrivePadMask_DrvUp); - } - } - - void UpdateCzDrvDn(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvDn, PinmuxDrivePadMask_CzDrvDn); - } - } - - void UpdateCzDrvUp(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvUp, PinmuxDrivePadMask_CzDrvUp); - } - } - - void UpdateSlwR(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwR, PinmuxDrivePadMask_SlwR); - } - } - - void UpdateSlwF(u8 v) { - if (v != 0xFF) { - this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwF, PinmuxDrivePadMask_SlwF); - } - } - public: - constexpr PinmuxDrivePad() : reg_address(), reg_mask(), reg_value(), pad_name() { /* ... */ } - - void UpdatePinmuxDrivePad(u32 config, u32 config_mask) { - /* Update register value. */ - this->reg_value = this->ReadReg(); - - /* Update drvdn. */ - if ((config_mask & PinmuxDriveOptBitMask_DrvDn) != 0) { - if (this->IsCzDrvDn()) { - const auto opt = (config & PinmuxDriveOptBitMask_CzDrvDn); - this->UpdateCzDrvDn(opt >> PinmuxDriveOptBitOffset_CzDrvDn); - } else { - const auto opt = (config & PinmuxDriveOptBitMask_DrvDn); - this->UpdateDrvDn(opt >> PinmuxDriveOptBitOffset_DrvDn); - } - } - - /* Update drvup. */ - if ((config_mask & PinmuxDriveOptBitMask_DrvUp) != 0) { - if (this->IsCzDrvUp()) { - const auto opt = (config & PinmuxDriveOptBitMask_CzDrvUp); - this->UpdateCzDrvUp(opt >> PinmuxDriveOptBitOffset_CzDrvUp); - } else { - const auto opt = (config & PinmuxDriveOptBitMask_DrvUp); - this->UpdateDrvUp(opt >> PinmuxDriveOptBitOffset_DrvUp); - } - } - - /* Update slwr */ - if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { - const auto opt = (config & PinmuxDriveOptBitMask_SlwR); - this->UpdateSlwR(opt >> PinmuxDriveOptBitOffset_SlwR); - } - - /* Update slwf */ - if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { - const auto opt = (config & PinmuxDriveOptBitMask_SlwF); - this->UpdateSlwF(opt >> PinmuxDriveOptBitOffset_SlwF); - } - - /* Write the updated register value. */ - this->WriteReg(); - } - - void SetCharacter(const PinmuxDrivePadCharacter &character) { - this->reg_address = character.reg_offset + 0x70000000; - this->reg_mask = character.reg_mask; - this->reg_value = this->ReadReg(); - this->pad_name = character.pad_name; - } - }; - - constinit std::array g_pinmux_pads{}; - constinit std::array g_pinmux_drive_pads{}; - - } - - void InitializePlatformPads() { - /* Initialize all pads. */ - for (size_t i = 0; i < NumPinmuxPadCharacters; ++i) { - g_pinmux_pads[i].SetCharacter(PinmuxPadCharacters[i]); - } - - /* Update all drive pads. */ - for (size_t i = 0; i < NumPinmuxDrivePadCharacters; ++i) { - g_pinmux_drive_pads[i].SetCharacter(PinmuxDrivePadCharacters[i]); - } - } - - void UpdateSinglePinmuxPad(const PinmuxPadConfig &config) { - if (IsInitialized()) { - g_pinmux_pads[config.index].UpdatePinmuxPad(config.option, config.option_mask); - } - } - - void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config) { - if (IsInitialized()) { - g_pinmux_drive_pads[config.index].UpdatePinmuxDrivePad(config.option, config.option_mask); - } - } - -} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp deleted file mode 100644 index 144bf52..0000000 --- a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::pinmux::driver::board::nintendo_nx { - - struct PinmuxPadConfig { - u32 index; - u32 option; - u32 option_mask; - }; - - struct PinmuxDrivePadConfig { - u32 index; - u32 option; - u32 option_mask; - }; - - void InitializePlatformPads(); - - void UpdateSinglePinmuxPad(const PinmuxPadConfig &config); - void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config); - -} diff --git a/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp b/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp index 1bf2c9a..1da07ae 100644 --- a/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp +++ b/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp @@ -18,7 +18,7 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include "board/nintendo_nx/pinmux_board_driver_api.hpp" + #include "board/nintendo/nx/pinmux_board_driver_api.hpp" namespace ams::pinmux::driver::board { using namespace ams::pinmux::driver::board::nintendo_nx; } diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.cpp new file mode 100644 index 0000000..c1b3be2 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.cpp @@ -0,0 +1,474 @@ +/* + * 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 "../../../powctl_device_management.hpp" +#include "powctl_retry_helper.hpp" +#include "powctl_battery_driver.hpp" +#include "powctl_max17050_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_battery_device; + + Max17050Driver &GetMax17050Driver() { + static Max17050Driver s_max17050_driver; + return s_max17050_driver; + } + + constexpr inline const double SenseResistorValue = 0.005; + + } + + BatteryDevice::BatteryDevice(bool ev) : use_event_handler(ev), event_handler() { + if (this->use_event_handler) { + /* Create the system event. */ + os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); + + /* Create the handler. */ + this->event_handler.emplace(this); + + /* Register the event handler. */ + powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); + } + } + + /* Generic API. */ + void BatteryDriver::InitializeDriver() { + /* Initialize gpio library. */ + gpio::Initialize(); + + /* Create battery device. */ + g_battery_device.emplace(this->IsEventHandlerEnabled()); + + /* Initialize the Max17050Driver. */ + { + size_t battery_vendor_size; + char battery_vendor[0x18] = {}; + if (R_FAILED(cal::GetBatteryVendor(std::addressof(battery_vendor_size), battery_vendor, sizeof(battery_vendor)))) { + battery_vendor[7] = 'A'; + battery_vendor_size = 0; + } + + u8 battery_version = 0; + if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) { + battery_version = 0; + } + + GetMax17050Driver().Initialize(battery_vendor, battery_version); + } + + /* Register our device. */ + this->RegisterDevice(std::addressof(*g_battery_device)); + + /* Register the charger device's code. */ + R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Max17050, std::addressof(*g_battery_device))); + + } + + void BatteryDriver::FinalizeDriver() { + /* Unregister the charger device code. */ + powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Max17050); + + /* Unregister our device. */ + this->UnregisterDevice(std::addressof(*g_battery_device)); + + /* Finalize Max17050Driver. */ + GetMax17050Driver().Finalize(); + + /* Destroy the charger device. */ + g_battery_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); + } + + Result BatteryDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check that we support event handlers. */ + R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); + + *out = device->SafeCastTo().GetSystemEvent(); + return ResultSuccess(); + } + + Result BatteryDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the interrupt enable. */ + device->SafeCastTo().SetInterruptEnabled(enable); + + return ResultSuccess(); + } + + Result BatteryDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + Result BatteryDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { + /* TODO */ + AMS_ABORT(); + } + + Result BatteryDriver::GetBatterySocRep(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocRep(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatterySocVf(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocVf(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryFullCapacity(int *out_mah, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetFullCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryRemainingCapacity(int *out_mah, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetRemainingCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMinimumAlertThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMaximumAlertThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageFullThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageFullThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageCurrent(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryCurrent(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(out_size != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ReadInternalState()); + GetMax17050Driver().GetInternalState(static_cast(dst)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(src), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + GetMax17050Driver().SetInternalState(*static_cast(src)); + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().WriteInternalState()); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetNeedToRestoreParameters(out)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryNeedToRestoreParameters(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetNeedToRestoreParameters(en)); + + return ResultSuccess(); + } + + Result BatteryDriver::IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().IsI2cShutdownEnabled(out)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryI2cShutdownEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetI2cShutdownEnabled(en)); + + return ResultSuccess(); + } + + Result BatteryDriver::IsBatteryPresent(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery status. */ + u16 status; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetStatus(std::addressof(status))); + + /* Set output. */ + *out = (status & 0x0008) == 0; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryCycles(int *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery cycles. */ + u16 cycles; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCycles(std::addressof(cycles))); + + /* Set output. */ + *out = cycles; + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryCycles(IDevice *device, int cycles) { + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(cycles == 0, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ResetCycles()); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAge(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAge(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryTemperature(float *out_c, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetTemperature(std::addressof(temp))); + + /* Set output. */ + *out_c = temp; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryMaximumTemperature(float *out_c, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + u8 max_temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetMaximumTemperature(std::addressof(max_temp))); + + /* Set output. */ + *out_c = static_cast(max_temp); + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMinimumAlertThreshold(c)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMaximumAlertThreshold(c)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryVCell(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetVCell(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageVCell(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCell(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ms; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCellTime(std::addressof(ms))); + + /* Set output. */ + *out = TimeSpan::FromMicroSeconds(static_cast(ms * 1000.0)); + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMinimumAlertThreshold(mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetOpenCircuitVoltage(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMaximumAlertThreshold(mv)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.hpp new file mode 100644 index 0000000..1221e0b --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_battery_driver.hpp @@ -0,0 +1,145 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../../powctl_i_power_control_driver.hpp" +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + class BatteryDevice : public powctl::impl::IDevice { + NON_COPYABLE(BatteryDevice); + NON_MOVEABLE(BatteryDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDevice, ::ams::powctl::impl::IDevice); + private: + bool use_event_handler; + std::optional event_handler; + os::SystemEventType system_event; + public: + BatteryDevice(bool ev); + + os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } + + void SetInterruptEnabled(bool en) { + if (this->use_event_handler) { + this->event_handler->SetInterruptEnabled(en); + } + } + }; + + class BatteryDriver : public IPowerControlDriver { + NON_COPYABLE(BatteryDriver); + NON_MOVEABLE(BatteryDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDriver, ::ams::powctl::impl::IPowerControlDriver); + public: + BatteryDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } + + /* Generic API. */ + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; + virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; + + virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; + virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; + + /* Battery API. */ + virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override; + + virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override; + + virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override; + virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override; + + virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override; + virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override; + virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override; + + virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override; + virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override; + + virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override; + virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override; + + virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override; + virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override; + + virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override; + virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override; + + virtual Result IsBatteryPresent(bool *out, IDevice *device) override; + + virtual Result GetBatteryCycles(int *out, IDevice *device) override; + virtual Result SetBatteryCycles(IDevice *device, int cycles) override; + + virtual Result GetBatteryAge(float *out_percent, IDevice *device) override; + + virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override; + virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override; + + virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override; + virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override; + + virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override; + virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override; + + virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override; + + virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override; + + virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override; + + virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override; + + /* Unsupported Charger API. */ + virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override { return powctl::ResultNotSupported(); } + + virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override { return powctl::ResultNotSupported(); } + virtual Result ResetChargerWatchdogTimer(IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.cpp new file mode 100644 index 0000000..4fe399e --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.cpp @@ -0,0 +1,73 @@ +/* + * 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 "../../../powctl_device_management.hpp" +#include "powctl_board_impl.hpp" +#include "powctl_battery_driver.hpp" +#include "powctl_charger_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_charger_driver; + constinit std::optional g_battery_driver; + + void InitializeChargerDriver(bool use_event_handlers) { + /* Create the charger driver. */ + g_charger_driver.emplace(use_event_handlers); + + /* Register the driver. */ + powctl::impl::RegisterDriver(std::addressof(*g_charger_driver)); + } + + void InitializeBatteryDriver(bool use_event_handlers) { + /* Create the battery driver. */ + g_battery_driver.emplace(use_event_handlers); + + /* Register the driver. */ + powctl::impl::RegisterDriver(std::addressof(*g_battery_driver)); + } + + void FinalizeChargerDriver() { + /* Unregister the driver. */ + powctl::impl::UnregisterDriver(std::addressof(*g_charger_driver)); + + /* Destroy the battery driver. */ + g_charger_driver = std::nullopt; + } + + void FinalizeBatteryDriver() { + /* Unregister the driver. */ + powctl::impl::UnregisterDriver(std::addressof(*g_battery_driver)); + + /* Destroy the battery driver. */ + g_battery_driver = std::nullopt; + } + + } + + void Initialize(bool use_event_handlers) { + InitializeChargerDriver(use_event_handlers); + InitializeBatteryDriver(use_event_handlers); + } + + void Finalize() { + FinalizeBatteryDriver(); + FinalizeChargerDriver(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.hpp new file mode 100644 index 0000000..902c5e9 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_board_impl.hpp @@ -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 . + */ +#pragma once +#include + +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + void Initialize(bool use_event_handlers); + void Finalize(); + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.cpp new file mode 100644 index 0000000..32fa8a9 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.cpp @@ -0,0 +1,413 @@ +/* + * 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 "powctl_bq24193_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace bq24193 { + + constexpr inline u8 InputSourceControl = 0x00; + constexpr inline u8 PowerOnConfiguration = 0x01; + constexpr inline u8 ChargeCurrentControl = 0x02; + constexpr inline u8 PreChargeTerminationCurrentControl = 0x03; + constexpr inline u8 ChargeVoltageControl = 0x04; + constexpr inline u8 ChargeTerminationTimerControl = 0x05; + constexpr inline u8 IrCompensationThermalRegulationControl = 0x06; + constexpr inline u8 MiscOperationControl = 0x07; + constexpr inline u8 SystemStatus = 0x08; + constexpr inline u8 Fault = 0x09; + constexpr inline u8 VendorPartRevisionStatus = 0x0A; + + constexpr u8 EncodePreChargeCurrentLimit(int ma) { + constexpr int Minimum = 128; + constexpr int Maximum = 2048; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 7) << 4); + } + + constexpr u8 EncodeTerminationCurrentLimit(int ma) { + constexpr int Minimum = 128; + constexpr int Maximum = 2048; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 7) << 0); + } + + constexpr u8 EncodeMinimumSystemVoltageLimit(int mv) { + constexpr int Minimum = 3000; + constexpr int Maximum = 3700; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast(((mv - Minimum) / 100) << 1); + } + + constexpr u8 EncodeFastChargeCurrentLimit(int ma) { + constexpr int Minimum = 512; + constexpr int Maximum = 4544; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 6) << 2); + } + + constexpr int DecodeFastChargeCurrentLimit(u8 reg) { + constexpr int Minimum = 512; + + return Minimum + (static_cast(reg & 0xFC) << 4); + } + + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(512)) == 512); + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(4544)) == 4544); + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(576)) == 576); + + constexpr u8 EncodeChargeVoltageLimit(int mv) { + constexpr int Minimum = 3504; + constexpr int Maximum = 4400; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast((static_cast(mv - Minimum) >> 4) << 2); + } + + constexpr int DecodeChargeVoltageLimit(u8 reg) { + constexpr int Minimum = 3504; + + return Minimum + (static_cast(reg & 0xFC) << 2); + } + + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3504)) == 3504); + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(4400)) == 4400); + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3520)) == 3520); + + constexpr u8 EncodeChargerConfiguration(bq24193::ChargerConfiguration cfg) { + switch (cfg) { + case ChargerConfiguration_ChargeDisable: return 0x00; + case ChargerConfiguration_ChargeBattery: return 0x10; + case ChargerConfiguration_Otg: return 0x20; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr u8 EncodeWatchdogTimerSetting(int seconds) { + if (seconds == 0) { + return 0x00; + } else if (seconds < 80) { + return 0x10; + } else if (seconds < 160) { + return 0x20; + } else { + return 0x30; + } + } + + constexpr u8 EncodeBatteryCompensation(int mo) { + constexpr int Minimum = 0; + constexpr int Maximum = 70; + mo = std::max(std::min(mo, Maximum), Minimum); + + return static_cast((static_cast(mo - Minimum) / 10) << 5); + } + + constexpr int DecodeBatteryCompensation(u8 reg) { + constexpr int Minimum = 0; + + return Minimum + (static_cast(reg & 0xE0) >> 5) * 10; + } + + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(0)) == 0); + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(70)) == 70); + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(30)) == 30); + + constexpr u8 EncodeVoltageClamp(int mv) { + constexpr int Minimum = 0; + constexpr int Maximum = 112; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast((static_cast(mv - Minimum) >> 4) << 2); + } + + constexpr int DecodeVoltageClamp(u8 reg) { + constexpr int Minimum = 0; + + return Minimum + (static_cast(reg & 0x1C) << 2); + } + + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(0)) == 0); + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(112)) == 112); + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(64)) == 64); + + constexpr u8 EncodeInputCurrentLimit(int ma) { + if (ma < 150) { + return 0; + } else if (ma < 500) { + return 1; + } else if (ma < 900) { + return 2; + } else if (ma < 1200) { + return 3; + } else if (ma < 1500) { + return 4; + } else if (ma < 2000) { + return 5; + } else if (ma < 3000) { + return 6; + } else{ + return 7; + } + } + + constexpr int DecodeInputCurrentLimit(u8 reg) { + switch (reg & 0x07) { + case 0: return 100; + case 1: return 150; + case 2: return 500; + case 3: return 900; + case 4: return 1200; + case 5: return 1500; + case 6: return 2000; + case 7: return 3000; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(100)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(150)) == 150); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(500)) == 500); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(900)) == 900); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1200)) == 1200); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1500)) == 1500); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(2000)) == 2000); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(3000)) == 3000); + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(0)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(9999)) == 3000); + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(149)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(151)) == 150); + + constexpr u8 EncodeInputVoltageLimit(int mv) { + constexpr int Minimum = 3880; + constexpr int Maximum = 5080; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast(((static_cast(mv - Minimum) / 80) & 0xF) << 3); + } + + constexpr u8 EncodeBoostModeCurrentLimit(int ma) { + return ma >= 1300 ? 1 : 0; + } + + constexpr bq24193::ChargerStatus DecodeChargerStatus(u8 reg) { + switch (reg & 0x30) { + case 0x00: return bq24193::ChargerStatus_NotCharging; + case 0x10: return bq24193::ChargerStatus_PreCharge; + case 0x20: return bq24193::ChargerStatus_FastCharging; + case 0x30: return bq24193::ChargerStatus_ChargeTerminationDone; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + namespace { + + ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u8 mask, u8 value) { + /* Read the current value. */ + u8 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u8 new_val = (cur_val & ~mask) | (value & mask); + R_TRY(i2c::WriteSingleRegister(session, address, new_val)); + + return ResultSuccess(); + } + + } + + Result Bq24193Driver::InitializeSession() { + /* Set fast charge current limit. */ + R_TRY(this->SetFastChargeCurrentLimit(512)); + + /* Disable force 20 percent charge. */ + R_TRY(this->SetForce20PercentChargeCurrent(false)); + + /* Set pre-charge current limit. */ + R_TRY(this->SetPreChargeCurrentLimit(128)); + + /* Set termination current limit. */ + R_TRY(this->SetTerminationCurrentLimit(128)); + + /* Set minimum system voltage limit. */ + R_TRY(this->SetMinimumSystemVoltageLimit(3000)); + + /* Set watchdog timer setting. */ + R_TRY(this->SetWatchdogTimerSetting(0)); + + /* Disable charging safety timer. */ + R_TRY(this->SetChargingSafetyTimerEnabled(false)); + + /* Reset the watchdog timer. */ + R_TRY(this->ResetWatchdogTimer()); + + return ResultSuccess(); + } + + Result Bq24193Driver::SetPreChargeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(ma)); + } + + Result Bq24193Driver::SetTerminationCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(ma)); + } + + Result Bq24193Driver::SetMinimumSystemVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(mv)); + } + + Result Bq24193Driver::SetChargingSafetyTimerEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x08, en ? 0x08 : 0x00); + } + + Result Bq24193Driver::GetForce20PercentChargeCurrent(bool *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x01) != 0; + return ResultSuccess(); + } + + Result Bq24193Driver::SetForce20PercentChargeCurrent(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0x01, en ? 0x01 : 0x00); + } + + Result Bq24193Driver::GetFastChargeCurrentLimit(int *out_ma) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); + + /* Extract the value. */ + *out_ma = bq24193::DecodeFastChargeCurrentLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetFastChargeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(ma)); + } + + Result Bq24193Driver::GetChargeVoltageLimit(int *out_mv) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeVoltageControl, std::addressof(val))); + + /* Extract the value. */ + *out_mv = bq24193::DecodeChargeVoltageLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetChargeVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(mv)); + } + + Result Bq24193Driver::SetChargerConfiguration(bq24193::ChargerConfiguration cfg) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x30, bq24193::EncodeChargerConfiguration(cfg)); + } + + Result Bq24193Driver::IsHiZEnabled(bool *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x80) != 0; + return ResultSuccess(); + } + + Result Bq24193Driver::SetHiZEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x80, en ? 0x80 : 0x00); + } + + Result Bq24193Driver::GetInputCurrentLimit(int *out_ma) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); + + /* Extract the value. */ + *out_ma = bq24193::DecodeInputCurrentLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetInputCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x07, bq24193::EncodeInputCurrentLimit(ma)); + } + + Result Bq24193Driver::SetInputVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x78, bq24193::EncodeInputVoltageLimit(mv)); + } + + Result Bq24193Driver::SetBoostModeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x01, bq24193::EncodeBoostModeCurrentLimit(ma)); + } + + Result Bq24193Driver::GetChargerStatus(bq24193::ChargerStatus *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::SystemStatus, std::addressof(val))); + + /* Extract the value. */ + *out = bq24193::DecodeChargerStatus(val); + return ResultSuccess(); + } + + Result Bq24193Driver::ResetWatchdogTimer() { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x40, 0x40); + } + + Result Bq24193Driver::SetWatchdogTimerSetting(int seconds) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x30, bq24193::EncodeWatchdogTimerSetting(seconds)); + } + + Result Bq24193Driver::GetBatteryCompensation(int *out_mo) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); + + /* Extract the value. */ + *out_mo = bq24193::DecodeBatteryCompensation(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetBatteryCompensation(int mo) { + return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0xE0, bq24193::EncodeBatteryCompensation(mo)); + } + + Result Bq24193Driver::GetVoltageClamp(int *out_mv) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); + + /* Extract the value. */ + *out_mv = bq24193::DecodeVoltageClamp(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetVoltageClamp(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0x1C, bq24193::EncodeVoltageClamp(mv)); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.hpp new file mode 100644 index 0000000..01d9b90 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_bq24193_driver.hpp @@ -0,0 +1,115 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace bq24193 { + + enum ChargerConfiguration { + ChargerConfiguration_ChargeDisable = 0, + ChargerConfiguration_ChargeBattery = 1, + ChargerConfiguration_Otg = 2, + }; + + enum ChargerStatus { + ChargerStatus_NotCharging = 0, + ChargerStatus_PreCharge = 1, + ChargerStatus_FastCharging = 2, + ChargerStatus_ChargeTerminationDone = 3, + }; + + } + + class Bq24193Driver { + private: + os::SdkMutex mutex; + int init_count; + i2c::I2cSession i2c_session; + private: + Result InitializeSession(); + public: + Bq24193Driver() : mutex(), init_count(0), i2c_session() { + /* ... */ + } + + void Initialize() { + std::scoped_lock lk(this->mutex); + if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193)); + + /* Initialize session. */ + R_ABORT_UNLESS(this->InitializeSession()); + } + } + + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + + Result SetPreChargeCurrentLimit(int ma); + Result SetTerminationCurrentLimit(int ma); + + Result SetMinimumSystemVoltageLimit(int mv); + + Result SetChargingSafetyTimerEnabled(bool en); + + Result GetForce20PercentChargeCurrent(bool *out); + Result SetForce20PercentChargeCurrent(bool en); + + Result GetFastChargeCurrentLimit(int *out_ma); + Result SetFastChargeCurrentLimit(int ma); + + Result GetChargeVoltageLimit(int *out_mv); + Result SetChargeVoltageLimit(int mv); + + Result SetChargerConfiguration(bq24193::ChargerConfiguration cfg); + + Result IsHiZEnabled(bool *out); + Result SetHiZEnabled(bool en); + + Result GetInputCurrentLimit(int *out_ma); + Result SetInputCurrentLimit(int ma); + + Result SetInputVoltageLimit(int mv); + + Result SetBoostModeCurrentLimit(int ma); + + Result GetChargerStatus(bq24193::ChargerStatus *out); + + Result ResetWatchdogTimer(); + Result SetWatchdogTimerSetting(int seconds); + + Result GetBatteryCompensation(int *out_mo); + Result SetBatteryCompensation(int mo); + + Result GetVoltageClamp(int *out_mv); + Result SetVoltageClamp(int mv); + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.cpp new file mode 100644 index 0000000..08e1c9a --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.cpp @@ -0,0 +1,369 @@ +/* + * 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 "../../../powctl_device_management.hpp" +#include "powctl_retry_helper.hpp" +#include "powctl_charger_driver.hpp" +#include "powctl_bq24193_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_charger_device; + + Bq24193Driver &GetBq24193Driver() { + static Bq24193Driver s_bq24193_driver; + return s_bq24193_driver; + } + + } + + ChargerDevice::ChargerDevice(bool ev) : gpio_pad_session(), watchdog_timer_enabled(false), watchdog_timer_timeout(0), use_event_handler(ev), event_handler() { + if (this->use_event_handler) { + /* Create the system event. */ + os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); + + /* Create the handler. */ + this->event_handler.emplace(this); + + /* Register the event handler. */ + powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); + } + } + + /* Generic API. */ + void ChargerDriver::InitializeDriver() { + /* Initialize Bq24193Driver */ + GetBq24193Driver().Initialize(); + + /* Initialize gpio library. */ + gpio::Initialize(); + + /* Create charger device. */ + g_charger_device.emplace(this->IsEventHandlerEnabled()); + + /* Open the device's gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(g_charger_device->GetPadSession(), gpio::DeviceCode_BattChgEnableN)); + + /* Configure the gpio session as output. */ + gpio::SetDirection(g_charger_device->GetPadSession(), gpio::Direction_Output); + + /* Register our device. */ + this->RegisterDevice(std::addressof(*g_charger_device)); + + /* Register the charger device's code. */ + R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Bq24193, std::addressof(*g_charger_device))); + } + + void ChargerDriver::FinalizeDriver() { + /* Unregister the charger device code. */ + powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Bq24193); + + /* Unregister our device. */ + this->UnregisterDevice(std::addressof(*g_charger_device)); + + /* Close the device's gpio session. */ + gpio::CloseSession(g_charger_device->GetPadSession()); + + /* Destroy the charger device. */ + g_charger_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); + + /* Finalize Bq24193Driver. */ + GetBq24193Driver().Finalize(); + } + + Result ChargerDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check that we support event handlers. */ + R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); + + *out = device->SafeCastTo().GetSystemEvent(); + return ResultSuccess(); + } + + Result ChargerDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the interrupt enable. */ + device->SafeCastTo().SetInterruptEnabled(enable); + + return ResultSuccess(); + } + + Result ChargerDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + Result ChargerDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { + /* TODO */ + AMS_ABORT(); + } + + /* Charger API. */ + Result ChargerDriver::GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check if we're not charging. */ + if (gpio::GetValue(device->SafeCastTo().GetPadSession()) == gpio::GpioValue_High) { + *out = ChargeCurrentState_NotCharging; + } else { + /* Get force 20 percent charge state. */ + bool force_20_percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetForce20PercentChargeCurrent(std::addressof(force_20_percent))); + + /* Set output appropriately. */ + if (force_20_percent) { + *out = ChargeCurrentState_ChargingForce20Percent; + } else { + *out = ChargeCurrentState_Charging; + } + } + + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + switch (state) { + case ChargeCurrentState_NotCharging: + gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_High); + break; + case ChargeCurrentState_ChargingForce20Percent: + case ChargeCurrentState_Charging: + gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_Low); + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetForce20PercentChargeCurrent(state == ChargeCurrentState_ChargingForce20Percent)); + break; + case ChargeCurrentState_Unknown: + return powctl::ResultInvalidArgument(); + } + + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetFastChargeCurrentLimit(out_ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerFastChargeCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetFastChargeCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargeVoltageLimit(out_mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargeVoltageLimit(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargeVoltageLimit(mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + bq24193::ChargerConfiguration bq_cfg; + switch (cfg) { + case ChargerConfiguration_ChargeDisable: bq_cfg = bq24193::ChargerConfiguration_ChargeDisable; break; + case ChargerConfiguration_ChargeBattery: bq_cfg = bq24193::ChargerConfiguration_ChargeBattery; break; + case ChargerConfiguration_Otg: bq_cfg = bq24193::ChargerConfiguration_Otg; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargerConfiguration(bq_cfg)); + return ResultSuccess(); + } + + Result ChargerDriver::IsChargerHiZEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().IsHiZEnabled(out)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerHiZEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetHiZEnabled(en)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerInputCurrentLimit(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetInputCurrentLimit(out_ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerInputCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerInputVoltageLimit(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputVoltageLimit(mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerBoostModeCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBoostModeCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerChargerStatus(ChargerStatus *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + bq24193::ChargerStatus bq_status; + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargerStatus(std::addressof(bq_status))); + + switch (bq_status) { + case bq24193::ChargerStatus_NotCharging: + *out = ChargerStatus_NotCharging; + break; + case bq24193::ChargerStatus_PreCharge: + case bq24193::ChargerStatus_FastCharging: + *out = ChargerStatus_Charging; + break; + case bq24193::ChargerStatus_ChargeTerminationDone: + *out = ChargerStatus_ChargeTerminationDone; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result ChargerDriver::IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + *out = device->SafeCastTo().IsWatchdogTimerEnabled(); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerWatchdogTimerEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + auto &charger_device = device->SafeCastTo(); + + if (en) { + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(charger_device.GetWatchdogTimerTimeout().GetSeconds())); + } else { + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(0)); + } + + charger_device.SetWatchdogTimerEnabled(en); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + device->SafeCastTo().SetWatchdogTimerTimeout(timeout); + return ResultSuccess(); + } + + Result ChargerDriver::ResetChargerWatchdogTimer(IDevice *device) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerBatteryCompensation(int *out_mo, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mo != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetBatteryCompensation(out_mo)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerBatteryCompensation(IDevice *device, int mo) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBatteryCompensation(mo)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerVoltageClamp(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetVoltageClamp(out_mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerVoltageClamp(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetVoltageClamp(mv)); + return ResultSuccess(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.hpp new file mode 100644 index 0000000..680d44c --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_charger_driver.hpp @@ -0,0 +1,156 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../../powctl_i_power_control_driver.hpp" +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + class ChargerDevice : public powctl::impl::IDevice { + NON_COPYABLE(ChargerDevice); + NON_MOVEABLE(ChargerDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDevice, ::ams::powctl::impl::IDevice); + private: + gpio::GpioPadSession gpio_pad_session; + bool watchdog_timer_enabled; + TimeSpan watchdog_timer_timeout; + bool use_event_handler; + std::optional event_handler; + os::SystemEventType system_event; + public: + ChargerDevice(bool ev); + + bool IsWatchdogTimerEnabled() const { return this->watchdog_timer_enabled; } + void SetWatchdogTimerEnabled(bool en) { this->watchdog_timer_enabled = en; } + + TimeSpan GetWatchdogTimerTimeout() const { return this->watchdog_timer_timeout; } + void SetWatchdogTimerTimeout(TimeSpan ts) { this->watchdog_timer_timeout = ts; } + + gpio::GpioPadSession *GetPadSession() { return std::addressof(this->gpio_pad_session); } + + os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } + + void SetInterruptEnabled(bool en) { + if (this->use_event_handler) { + this->event_handler->SetInterruptEnabled(en); + } + } + }; + + class ChargerDriver : public IPowerControlDriver { + NON_COPYABLE(ChargerDriver); + NON_MOVEABLE(ChargerDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDriver, ::ams::powctl::impl::IPowerControlDriver); + public: + ChargerDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } + + /* Generic API. */ + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; + virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; + + virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; + virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; + + /* Charger API. */ + virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override; + virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override; + + virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override; + virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override; + + virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override; + virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override; + + virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override; + + virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override; + virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override; + + virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override; + virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override; + + virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override; + + virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override; + + virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override; + + virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override; + virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override; + + virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override; + virtual Result ResetChargerWatchdogTimer(IDevice *device) override; + + virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override; + virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override; + + virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override; + virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override; + + /* Unsupported Battery API. */ + virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result IsBatteryPresent(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryCycles(int *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryCycles(IDevice *device, int cycles) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAge(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.cpp new file mode 100644 index 0000000..360abec --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.cpp @@ -0,0 +1,31 @@ +/* + * 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 "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + void ChargerInterruptEventHandler::SignalEvent(IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + void BatteryInterruptEventHandler::SignalEvent(IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.hpp new file mode 100644 index 0000000..b445302 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_interrupt_event_handler.hpp @@ -0,0 +1,104 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../../powctl_i_power_control_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + template + class InterruptEventHandler : public ddsf::IEventHandler { + private: + IDevice *device; + gpio::GpioPadSession gpio_session; + os::SystemEventType gpio_system_event; + os::SdkMutex mutex; + public: + InterruptEventHandler(IDevice *dv) : IEventHandler(), device(dv), mutex() { + /* Initialize the gpio session. */ + Derived::Initialize(std::addressof(this->gpio_session), std::addressof(this->gpio_system_event)); + + /* Initialize ourselves as an event handler. */ + IEventHandler::Initialize(std::addressof(this->gpio_system_event)); + } + + os::SystemEventType *GetSystemEvent() { + return std::addressof(this->gpio_system_event); + } + + void SetInterruptEnabled(bool en) { + std::scoped_lock lk(this->mutex); + + gpio::SetInterruptEnable(std::addressof(this->gpio_session), en); + } + + virtual void HandleEvent() override final { + /* Acquire exclusive access to ourselves. */ + std::scoped_lock lk(this->mutex); + + /* Clear our interrupt status. */ + gpio::ClearInterruptStatus(std::addressof(this->gpio_session)); + + /* Clear our system event. */ + os::ClearSystemEvent(std::addressof(this->gpio_system_event)); + + /* Signal the event. */ + static_cast(this)->SignalEvent(this->device); + } + }; + + class ChargerInterruptEventHandler : public InterruptEventHandler { + friend class InterruptEventHandler; + private: + static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { + /* Open the gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_Bq24190Irq)); + + /* Configure the gpio session. */ + gpio::SetDirection(session, gpio::Direction_Input); + gpio::SetInterruptMode(session, gpio::InterruptMode_FallingEdge); + gpio::SetInterruptEnable(session, true); + + /* Bind the interrupt event. */ + R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); + } + + void SignalEvent(IDevice *device); + public: + ChargerInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } + }; + + class BatteryInterruptEventHandler : public InterruptEventHandler { + friend class InterruptEventHandler; + private: + static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { + /* Open the gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_BattMgicIrq)); + + /* Configure the gpio session. */ + gpio::SetDirection(session, gpio::Direction_Input); + gpio::SetInterruptMode(session, gpio::InterruptMode_LowLevel); + + /* Bind the interrupt event. */ + R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); + } + + void SignalEvent(IDevice *device); + public: + BatteryInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_custom_parameters.inc b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_custom_parameters.inc new file mode 100644 index 0000000..03c5a02 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_custom_parameters.inc @@ -0,0 +1,200 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by max17050_parameters_gen.py, do not edit manually. */ + +constexpr inline const CustomParameters CustomParameters0A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0053, + .tempco = 0x1C22, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5786, + .qresidual10 = 0x3184, + .qresidual20 = 0x1E00, + .qresidual30 = 0x1502, + .fullcap = 0x2476, + .vffullcap = 0x2476, + .modeltbl = { + 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, + 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, + 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, + 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0048, + .tempco = 0x2034, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5A00, + .qresidual10 = 0x3B00, + .qresidual20 = 0x0F80, + .qresidual30 = 0x0B02, + .fullcap = 0x2466, + .vffullcap = 0x2466, + .modeltbl = { + 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, + 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, + 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, + 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0085, + .tempco = 0x1625, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x3100, + .qresidual10 = 0x1B00, + .qresidual20 = 0x1000, + .qresidual30 = 0x0C81, + .fullcap = 0x227A, + .vffullcap = 0x227A, + .modeltbl = { + 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, + 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, + 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, + 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters1 = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0040, + .tempco = 0x1624, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4690, + .qresidual10 = 0x2605, + .qresidual20 = 0x1605, + .qresidual30 = 0x0F05, + .fullcap = 0x1AE4, + .vffullcap = 0x1AE4, + .modeltbl = { + 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, + 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, + 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, + 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1584, +}; + +constexpr inline const CustomParameters CustomParameters2A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004A, + .tempco = 0x1D23, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4000, + .qresidual10 = 0x1E80, + .qresidual20 = 0x0D83, + .qresidual30 = 0x0783, + .fullcap = 0x1C20, + .vffullcap = 0x1C20, + .modeltbl = { + 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, + 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, + 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, + 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x1680, +}; + +constexpr inline const CustomParameters CustomParameters2R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004C, + .tempco = 0x2D32, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5900, + .qresidual10 = 0x2900, + .qresidual20 = 0x1100, + .qresidual30 = 0x0B00, + .fullcap = 0x1CCE, + .vffullcap = 0x1CCE, + .modeltbl = { + 0x8E10, 0x9FC0, 0xA880, 0xB750, 0xBA10, 0xBB30, 0xBD20, 0xBE80, + 0xC0A0, 0xC350, 0xC670, 0xC8C0, 0xCCF0, 0xD050, 0xD140, 0xD5F0, + 0x0020, 0x00D0, 0x0200, 0x0E00, 0x1300, 0x1B00, 0x1930, 0x1150, + 0x0BF0, 0x07E0, 0x0AD0, 0x06F0, 0x07F0, 0x0EF0, 0x04F0, 0x04F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x170B, +}; + +constexpr inline const CustomParameters CustomParameters2M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0049, + .tempco = 0x222A, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4F00, + .qresidual10 = 0x2680, + .qresidual20 = 0x1205, + .qresidual30 = 0x0C87, + .fullcap = 0x1C68, + .vffullcap = 0x1C68, + .modeltbl = { + 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, + 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, + 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, + 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x16B9, +}; + diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.cpp new file mode 100644 index 0000000..424c65b --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.cpp @@ -0,0 +1,744 @@ +/* + * 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 "powctl_max17050_driver.hpp" + +#if defined(ATMOSPHERE_ARCH_ARM64) +#include +#endif + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + constexpr inline u8 Status = 0x00; + constexpr inline u8 VAlrtThreshold = 0x01; + constexpr inline u8 TAlrtThreshold = 0x02; + constexpr inline u8 SocAlrtThreshold = 0x03; + constexpr inline u8 AtRate = 0x04; + constexpr inline u8 RemCapRep = 0x05; + constexpr inline u8 SocRep = 0x06; + constexpr inline u8 Age = 0x07; + constexpr inline u8 Temperature = 0x08; + constexpr inline u8 VCell = 0x09; + constexpr inline u8 Current = 0x0A; + constexpr inline u8 AverageCurrent = 0x0B; + + constexpr inline u8 SocMix = 0x0D; + constexpr inline u8 SocAv = 0x0E; + constexpr inline u8 RemCapMix = 0x0F; + constexpr inline u8 FullCap = 0x10; + constexpr inline u8 Tte = 0x11; + constexpr inline u8 QResidual00 = 0x12; + constexpr inline u8 FullSocThr = 0x13; + + + constexpr inline u8 AverageTemp = 0x16; + constexpr inline u8 Cycles = 0x17; + constexpr inline u8 DesignCap = 0x18; + constexpr inline u8 AverageVCell = 0x19; + constexpr inline u8 MaxMinTemp = 0x1A; + constexpr inline u8 MaxMinVoltage = 0x1B; + constexpr inline u8 MaxMinCurrent = 0x1C; + constexpr inline u8 Config = 0x1D; + constexpr inline u8 IChgTerm = 0x1E; + constexpr inline u8 RemCapAv = 0x1F; + + constexpr inline u8 Version = 0x21; + constexpr inline u8 QResidual10 = 0x22; + constexpr inline u8 FullCapNom = 0x23; + constexpr inline u8 TempNom = 0x24; + constexpr inline u8 TempLim = 0x25; + + constexpr inline u8 Ain = 0x27; + constexpr inline u8 LearnCfg = 0x28; + constexpr inline u8 FilterCfg = 0x29; + constexpr inline u8 RelaxCfg = 0x2A; + constexpr inline u8 MiscCfg = 0x2B; + constexpr inline u8 TGain = 0x2C; + constexpr inline u8 TOff = 0x2D; + constexpr inline u8 CGain = 0x2E; + constexpr inline u8 COff = 0x2F; + + + constexpr inline u8 QResidual20 = 0x32; + + + constexpr inline u8 FullCap0 = 0x35; + constexpr inline u8 IAvgEmpty = 0x36; + constexpr inline u8 FCtc = 0x37; + constexpr inline u8 RComp0 = 0x38; + constexpr inline u8 TempCo = 0x39; + constexpr inline u8 VEmpty = 0x3A; + + + constexpr inline u8 FStat = 0x3D; + constexpr inline u8 Timer = 0x3E; + constexpr inline u8 ShdnTimer = 0x3F; + + + constexpr inline u8 QResidual30 = 0x42; + + + constexpr inline u8 DQAcc = 0x45; + constexpr inline u8 DPAcc = 0x46; + + constexpr inline u8 SocVf0 = 0x48; + + constexpr inline u8 Qh0 = 0x4C; + constexpr inline u8 Qh = 0x4D; + + constexpr inline u8 SocVfAccess = 0x60; + + constexpr inline u8 ModelAccess0 = 0x62; + constexpr inline u8 ModelAccess1 = 0x63; + + constexpr inline u8 ModelChrTblStart = 0x80; + constexpr inline u8 ModelChrTblEnd = 0xB0; + + + constexpr inline u8 VFocV = 0xFB; + constexpr inline u8 SocVf = 0xFF; + + constexpr inline size_t ModelChrTblSize = ModelChrTblEnd - ModelChrTblStart; + + namespace { + + struct CustomParameters { + u16 relaxcfg; + u16 rcomp0; + u16 tempco; + u16 ichgterm; + u16 tgain; + u16 toff; + u16 vempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + u16 fullcap; + u16 vffullcap; + u16 modeltbl[ModelChrTblSize]; + u16 fullsocthr; + u16 iavgempty; + }; + + #include "powctl_max17050_custom_parameters.inc" + + const CustomParameters &GetCustomParameters(const char *battery_vendor, u8 battery_version) { + if (battery_version == 2) { + if (battery_vendor[7] == 'M') { + return CustomParameters2M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters2R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters2A; + } + } else if (battery_version == 1) { + return CustomParameters1; + } else /* if (battery_version == 0) */ { + if (battery_vendor[7] == 'M') { + return CustomParameters0M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters0R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters0A; + } + } + } + + } + + } + + namespace { + + ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + R_TRY(i2c::WriteSingleRegister(session, address, new_val)); + + return ResultSuccess(); + } + + ALWAYS_INLINE Result ReadRegister(const i2c::I2cSession &session, u8 address, u16 *out) { + return i2c::ReadSingleRegister(session, address, out); + } + + ALWAYS_INLINE Result WriteRegister(const i2c::I2cSession &session, u8 address, u16 val) { + return i2c::WriteSingleRegister(session, address, val); + } + + ALWAYS_INLINE bool WriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 val) { + /* Write the value. */ + R_ABORT_UNLESS(WriteRegister(session, address, val)); + + /* Give it time to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(3)); + + /* Read it back. */ + u16 new_val; + R_ABORT_UNLESS(ReadRegister(session, address, std::addressof(new_val))); + + return new_val == val; + } + + ALWAYS_INLINE Result ReadWriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + while (!WriteValidateRegister(session, address, new_val)) { /* ... */ } + + return ResultSuccess(); + } + + double CoerceToDouble(u64 value) { + static_assert(sizeof(value) == sizeof(double)); + + double d; + __builtin_memcpy(std::addressof(d), std::addressof(value), sizeof(d)); + return d; + } + + double ExponentiateTwoToPower(s16 exponent, double scale) { + if (exponent >= 1024) { + exponent = exponent - 1023; + scale = scale * 8.98846567e307; + if (exponent >= 1024) { + exponent = std::min(exponent, 2046) - 1023; + scale = scale * 8.98846567e307; + } + } else if (exponent <= -1023) { + exponent = exponent + 969; + scale = scale * 2.00416836e-292; + if (exponent <= -1023) { + exponent = std::max(exponent, -1991) + 969; + scale = scale * 2.00416836e-292; + } + } + return scale * CoerceToDouble(static_cast(exponent + 1023) << 52); + } + + } + + Result Max17050Driver::InitializeSession(const char *battery_vendor, u8 battery_version) { + /* Get the custom parameters. */ + const auto ¶ms = max17050::GetCustomParameters(battery_vendor, battery_version); + + /* We only want to write the parameters on power on reset. */ + R_SUCCEED_IF(!this->IsPowerOnReset()); + + /* Set that we need to restore parameters. */ + R_TRY(this->SetNeedToRestoreParameters(true)); + + /* Wait for our configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(500)); + + /* Write initial config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::Config, 0x7210)); + + /* Write initial filter config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FilterCfg, 0x8784)); + + /* Write relax config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::RelaxCfg, params.relaxcfg)); + + /* Write initial learn config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::LearnCfg, 0x2603)); + + /* Write fullsocthr. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FullSocThr, params.fullsocthr)); + + /* Write iavgempty. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IAvgEmpty, params.iavgempty)); + + /* Unlock model table, write model table. */ + do { + R_TRY(this->UnlockModelTable()); + R_TRY(this->SetModelTable(params.modeltbl)); + } while (!this->IsModelTableSet(params.modeltbl)); + + /* Lock the model table, trying up to ten times. */ + { + size_t i = 0; + while (true) { + ++i; + + R_TRY(this->LockModelTable()); + + if (this->IsModelTableLocked()) { + break; + } + + R_SUCCEED_IF(i >= 10); + } + } + + /* Write and validate rcomp0 */ + while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, params.rcomp0)) { /* ... */ } + + /* Write and validate tempco */ + while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, params.tempco)) { /* ... */ } + + /* Write ichgterm. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IChgTerm, params.ichgterm)); + + /* Write tgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TGain, params.tgain)); + + /* Write toff. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TOff, params.toff)); + + /* Write and validate vempty. */ + while (!WriteValidateRegister(this->i2c_session, max17050::VEmpty, params.vempty)) { /* ... */ } + + /* Write and validate qresidual. */ + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, params.qresidual00)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, params.qresidual10)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, params.qresidual20)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, params.qresidual30)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Give some time for configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + /* Write vfsoc to vfsoc0, qh, to qh0. */ + u16 vfsoc, qh; + { + R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(vfsoc))); + R_TRY(this->UnlockVfSoc()); + while (!WriteValidateRegister(this->i2c_session, max17050::SocVf0, vfsoc)) { /* ... */ } + R_TRY(ReadRegister(this->i2c_session, max17050::Qh, std::addressof(qh))); + R_TRY(WriteRegister(this->i2c_session, max17050::Qh0, qh)); + R_TRY(this->LockVfSoc()); + } + + /* Reset cycles. */ + while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, 0x0060)) { /* ... */ } + + /* Load new capacity parameters. */ + const u16 remcap = static_cast((vfsoc * params.vffullcap) / 0x6400); + const u16 repcap = static_cast(remcap * (params.fullcap / params.vffullcap)); + const u16 dpacc = 0x0C80; + const u16 dqacc = params.vffullcap / 0x10; + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, remcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapRep, repcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, dpacc)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, dqacc)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Write soc rep. */ + R_TRY(WriteRegister(this->i2c_session, max17050::SocRep, vfsoc)); + + /* Clear power on reset. */ + R_TRY(ReadWriteValidateRegister(this->i2c_session, max17050::Status, 0x0002, 0x0000)); + + /* Set cgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::CGain, 0x7FFF)); + + return ResultSuccess(); + } + + Result Max17050Driver::SetMaximumShutdownTimerThreshold() { + return WriteRegister(this->i2c_session, max17050::ShdnTimer, 0xE000); + } + + bool Max17050Driver::IsPowerOnReset() { + /* Get the register. */ + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::Status, std::addressof(val))); + + /* Extract the value. */ + return (val & 0x0002) != 0; + } + + Result Max17050Driver::LockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0000); + } + + Result Max17050Driver::UnlockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0080); + } + + Result Max17050Driver::LockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0000)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x0000)); + return ResultSuccess(); + } + + Result Max17050Driver::UnlockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0059)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x00C4)); + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableLocked() { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != 0) { + return false; + } + } + + return true; + } + + Result Max17050Driver::SetModelTable(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelChrTblStart + i, model_table[i])); + } + + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableSet(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != model_table[i]) { + return false; + } + } + + return true; + } + + Result Max17050Driver::ReadInternalState() { + R_TRY(ReadRegister(this->i2c_session, max17050::RComp0, std::addressof(this->internal_state.rcomp0))); + R_TRY(ReadRegister(this->i2c_session, max17050::TempCo, std::addressof(this->internal_state.tempco))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(this->internal_state.fullcap))); + R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(this->internal_state.cycles))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCapNom, std::addressof(this->internal_state.fullcapnom))); + R_TRY(ReadRegister(this->i2c_session, max17050::IAvgEmpty, std::addressof(this->internal_state.iavgempty))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual00, std::addressof(this->internal_state.qresidual00))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual10, std::addressof(this->internal_state.qresidual10))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual20, std::addressof(this->internal_state.qresidual20))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual30, std::addressof(this->internal_state.qresidual30))); + return ResultSuccess(); + } + + Result Max17050Driver::WriteInternalState() { + while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, this->internal_state.rcomp0)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, this->internal_state.tempco)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, this->internal_state.fullcapnom)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::IAvgEmpty, this->internal_state.iavgempty)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, this->internal_state.qresidual00)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, this->internal_state.qresidual10)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, this->internal_state.qresidual20)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, this->internal_state.qresidual30)) { /* ... */ } + + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + u16 fullcap0, socmix; + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap0, std::addressof(fullcap0))); + R_TRY(ReadRegister(this->i2c_session, max17050::SocMix, std::addressof(socmix))); + + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, static_cast((fullcap0 * socmix) / 0x6400))) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, this->internal_state.fullcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, 0x0C80)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, this->internal_state.fullcapnom / 0x10)) { /* ... */ } + + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, this->internal_state.cycles)) { /* ... */ } + if (this->internal_state.cycles >= 0x100) { + while (!WriteValidateRegister(this->i2c_session, max17050::LearnCfg, 0x2673)) { /* ... */ } + } + + return ResultSuccess(); + } + + Result Max17050Driver::GetSocRep(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::SocRep, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetSocVf(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetFullCapacity(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, fullcap; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(fullcap))); + + /* Set output. */ + *out = ((static_cast(fullcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); + return ResultSuccess(); + } + + Result Max17050Driver::GetRemainingCapacity(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, remcap; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::RemCapRep, std::addressof(remcap))); + + /* Set output. */ + *out = ((static_cast(remcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); + return ResultSuccess(); + } + + Result Max17050Driver::SetPercentageMinimumAlertThreshold(int percentage) { + return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0x00FF, static_cast(percentage)); + } + + Result Max17050Driver::SetPercentageMaximumAlertThreshold(int percentage) { + return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0xFF00, static_cast(static_cast(percentage)) << 8); + } + + Result Max17050Driver::SetPercentageFullThreshold(double percentage) { + #if defined(ATMOSPHERE_ARCH_ARM64) + const u16 val = vcvtd_n_s64_f64(percentage, BITSIZEOF(u8)); + #else + #error "Unknown architecture for floating point -> fixed point" + #endif + + return WriteRegister(this->i2c_session, max17050::FullSocThr, val); + } + + Result Max17050Driver::GetAverageCurrent(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, coff, avg_current; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); + R_TRY(ReadRegister(this->i2c_session, max17050::AverageCurrent, std::addressof(avg_current))); + + /* Set output. */ + *out = (((static_cast(avg_current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetCurrent(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, coff, current; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); + R_TRY(ReadRegister(this->i2c_session, max17050::Current, std::addressof(current))); + + /* Set output. */ + *out = (((static_cast(current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetNeedToRestoreParameters(bool *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::MiscCfg, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x8000) != 0; + return ResultSuccess(); + } + + Result Max17050Driver::SetNeedToRestoreParameters(bool en) { + return ReadWriteRegister(this->i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0); + } + + Result Max17050Driver::IsI2cShutdownEnabled(bool *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Config, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x0040) != 0; + return ResultSuccess(); + } + + Result Max17050Driver::SetI2cShutdownEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, max17050::Config, 0x0040, en ? 0x0040 : 0); + } + + Result Max17050Driver::GetStatus(u16 *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + return ReadRegister(this->i2c_session, max17050::Status, out); + } + + Result Max17050Driver::GetCycles(u16 *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(val))); + + /* Extract the value. */ + *out = std::max(val, 0x60) - 0x60; + return ResultSuccess(); + } + + Result Max17050Driver::ResetCycles() { + return WriteRegister(this->i2c_session, max17050::Cycles, 0x0060); + } + + Result Max17050Driver::GetAge(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Age, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetTemperature(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Temperature, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetMaximumTemperature(u8 *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::MaxMinTemp, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val >> 8); + return ResultSuccess(); + } + + Result Max17050Driver::SetTemperatureMinimumAlertThreshold(int c) { + return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0x00FF, static_cast(c)); + } + + Result Max17050Driver::SetTemperatureMaximumAlertThreshold(int c) { + return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0xFF00, static_cast(static_cast(c)) << 8); + } + + Result Max17050Driver::GetVCell(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::VCell, std::addressof(val))); + + /* Set output. */ + *out = (625 * (val >> 3)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::GetAverageVCell(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::AverageVCell, std::addressof(val))); + + /* Set output. */ + *out = (625 * (val >> 3)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::GetAverageVCellTime(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::FilterCfg, std::addressof(val))); + + /* Set output. */ + *out = 175.8 * ExponentiateTwoToPower(6 + ((val >> 4) & 7), 1.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetOpenCircuitVoltage(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::VFocV, std::addressof(val))); + + /* Set output. */ + *out = (1250 * (val >> 4)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::SetVoltageMinimumAlertThreshold(int mv) { + return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0x00FF, static_cast(util::DivideUp(mv, 20))); + } + + Result Max17050Driver::SetVoltageMaximumAlertThreshold(int mv) { + return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0xFF00, static_cast(static_cast(mv / 20)) << 8); + } + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.hpp new file mode 100644 index 0000000..7d01e30 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_max17050_driver.hpp @@ -0,0 +1,144 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + struct InternalState { + u16 rcomp0; + u16 tempco; + u16 fullcap; + u16 cycles; + u16 fullcapnom; + u16 iavgempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + }; + + } + + class Max17050Driver { + private: + os::SdkMutex mutex; + int init_count; + i2c::I2cSession i2c_session; + max17050::InternalState internal_state; + private: + Result InitializeSession(const char *battery_vendor, u8 battery_version); + Result SetMaximumShutdownTimerThreshold(); + + bool IsPowerOnReset(); + Result LockVfSoc(); + Result UnlockVfSoc(); + Result LockModelTable(); + Result UnlockModelTable(); + bool IsModelTableLocked(); + Result SetModelTable(const u16 *model_table); + bool IsModelTableSet(const u16 *model_table); + public: + Max17050Driver() : mutex(), init_count(0), i2c_session(), internal_state() { + /* ... */ + } + + void Initialize(const char *battery_vendor, u8 battery_version) { + std::scoped_lock lk(this->mutex); + if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050)); + + /* Initialize session. */ + R_ABORT_UNLESS(this->InitializeSession(battery_vendor, battery_version)); + + /* Set shutdown timer threshold to the maximum value. */ + R_ABORT_UNLESS(this->SetMaximumShutdownTimerThreshold()); + } + } + + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + + Result ReadInternalState(); + Result WriteInternalState(); + + void GetInternalState(max17050::InternalState *dst) { + *dst = this->internal_state; + } + + void SetInternalState(const max17050::InternalState &src) { + this->internal_state = src; + } + + Result GetSocRep(double *out); + Result GetSocVf(double *out); + + Result GetFullCapacity(double *out, double sense_resistor); + Result GetRemainingCapacity(double *out, double sense_resistor); + + Result SetPercentageMinimumAlertThreshold(int percentage); + Result SetPercentageMaximumAlertThreshold(int percentage); + + Result SetPercentageFullThreshold(double percentage); + + Result GetAverageCurrent(double *out, double sense_resistor); + Result GetCurrent(double *out, double sense_resistor); + + Result GetNeedToRestoreParameters(bool *out); + Result SetNeedToRestoreParameters(bool en); + + Result IsI2cShutdownEnabled(bool *out); + Result SetI2cShutdownEnabled(bool en); + + Result GetStatus(u16 *out); + + Result GetCycles(u16 *out); + Result ResetCycles(); + + Result GetAge(double *out); + + Result GetTemperature(double *out); + + Result GetMaximumTemperature(u8 *out); + + Result SetTemperatureMinimumAlertThreshold(int c); + Result SetTemperatureMaximumAlertThreshold(int c); + + Result GetVCell(int *out); + Result GetAverageVCell(int *out); + Result GetAverageVCellTime(double *out); + + Result GetOpenCircuitVoltage(int *out); + + Result SetVoltageMinimumAlertThreshold(int mv); + Result SetVoltageMaximumAlertThreshold(int mv); + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_retry_helper.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_retry_helper.hpp new file mode 100644 index 0000000..d431099 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo/nx/powctl_retry_helper.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::powctl::impl { + + constexpr inline const TimeSpan PowerControlRetryTimeout = TimeSpan::FromSeconds(10); + constexpr inline const TimeSpan PowerControlRetryInterval = TimeSpan::FromMilliSeconds(20); + + #define AMS_POWCTL_R_TRY_WITH_RETRY(__EXPR__) \ + ({ \ + TimeSpan __powctl_retry_current_time = 0; \ + while (true) { \ + const Result __powctl_retry_result = (__EXPR__); \ + if (R_SUCCEEDED(__powctl_retry_result)) { \ + break; \ + } \ + \ + __powctl_retry_current_time += PowerControlRetryInterval; \ + R_UNLESS(__powctl_retry_current_time < PowerControlRetryTimeout, __powctl_retry_result); \ + \ + os::SleepThread(PowerControlRetryInterval); \ + } \ + }) + + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp deleted file mode 100644 index f346025..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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 "../../powctl_device_management.hpp" -#include "powctl_retry_helper.hpp" -#include "powctl_battery_driver.hpp" -#include "powctl_max17050_driver.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace { - - constinit std::optional g_battery_device; - - Max17050Driver &GetMax17050Driver() { - static Max17050Driver s_max17050_driver; - return s_max17050_driver; - } - - constexpr inline const double SenseResistorValue = 0.005; - - } - - BatteryDevice::BatteryDevice(bool ev) : use_event_handler(ev), event_handler() { - if (this->use_event_handler) { - /* Create the system event. */ - os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); - - /* Create the handler. */ - this->event_handler.emplace(this); - - /* Register the event handler. */ - powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); - } - } - - /* Generic API. */ - void BatteryDriver::InitializeDriver() { - /* Initialize gpio library. */ - gpio::Initialize(); - - /* Create battery device. */ - g_battery_device.emplace(this->IsEventHandlerEnabled()); - - /* Initialize the Max17050Driver. */ - { - size_t battery_vendor_size; - char battery_vendor[0x18] = {}; - if (R_FAILED(cal::GetBatteryVendor(std::addressof(battery_vendor_size), battery_vendor, sizeof(battery_vendor)))) { - battery_vendor[7] = 'A'; - battery_vendor_size = 0; - } - - u8 battery_version = 0; - if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) { - battery_version = 0; - } - - GetMax17050Driver().Initialize(battery_vendor, battery_version); - } - - /* Register our device. */ - this->RegisterDevice(std::addressof(*g_battery_device)); - - /* Register the charger device's code. */ - R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Max17050, std::addressof(*g_battery_device))); - - } - - void BatteryDriver::FinalizeDriver() { - /* Unregister the charger device code. */ - powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Max17050); - - /* Unregister our device. */ - this->UnregisterDevice(std::addressof(*g_battery_device)); - - /* Finalize Max17050Driver. */ - GetMax17050Driver().Finalize(); - - /* Destroy the charger device. */ - g_battery_device = std::nullopt; - - /* Finalize gpio library. */ - gpio::Finalize(); - } - - Result BatteryDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Check that we support event handlers. */ - R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); - - *out = device->SafeCastTo().GetSystemEvent(); - return ResultSuccess(); - } - - Result BatteryDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Set the interrupt enable. */ - device->SafeCastTo().SetInterruptEnabled(enable); - - return ResultSuccess(); - } - - Result BatteryDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); - } - - Result BatteryDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { - /* TODO */ - AMS_ABORT(); - } - - Result BatteryDriver::GetBatterySocRep(float *out_percent, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double percent; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocRep(std::addressof(percent))); - - /* Set output. */ - *out_percent = percent; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatterySocVf(float *out_percent, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double percent; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocVf(std::addressof(percent))); - - /* Set output. */ - *out_percent = percent; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryFullCapacity(int *out_mah, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double mah; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetFullCapacity(std::addressof(mah), SenseResistorValue)); - - /* Set output. */ - *out_mah = mah; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryRemainingCapacity(int *out_mah, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double mah; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetRemainingCapacity(std::addressof(mah), SenseResistorValue)); - - /* Set output. */ - *out_mah = mah; - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMinimumAlertThreshold(percentage)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMaximumAlertThreshold(percentage)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryPercentageFullThreshold(IDevice *device, float percentage) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageFullThreshold(percentage)); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryAverageCurrent(int *out_ma, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double ma; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageCurrent(std::addressof(ma), SenseResistorValue)); - - /* Set output. */ - *out_ma = ma; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryCurrent(int *out_ma, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double ma; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCurrent(std::addressof(ma), SenseResistorValue)); - - /* Set output. */ - *out_ma = ma; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(dst != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(out_size != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(dst_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); - R_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ReadInternalState()); - GetMax17050Driver().GetInternalState(static_cast(dst)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(src != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(src_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); - R_UNLESS(util::IsAligned(reinterpret_cast(src), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); - - GetMax17050Driver().SetInternalState(*static_cast(src)); - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().WriteInternalState()); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetNeedToRestoreParameters(out)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryNeedToRestoreParameters(IDevice *device, bool en) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Set the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetNeedToRestoreParameters(en)); - - return ResultSuccess(); - } - - Result BatteryDriver::IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().IsI2cShutdownEnabled(out)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryI2cShutdownEnabled(IDevice *device, bool en) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Set the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetI2cShutdownEnabled(en)); - - return ResultSuccess(); - } - - Result BatteryDriver::IsBatteryPresent(bool *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the battery status. */ - u16 status; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetStatus(std::addressof(status))); - - /* Set output. */ - *out = (status & 0x0008) == 0; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryCycles(int *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the battery cycles. */ - u16 cycles; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCycles(std::addressof(cycles))); - - /* Set output. */ - *out = cycles; - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryCycles(IDevice *device, int cycles) { - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(cycles == 0, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ResetCycles()); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryAge(float *out_percent, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double percent; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAge(std::addressof(percent))); - - /* Set output. */ - *out_percent = percent; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryTemperature(float *out_c, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double temp; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetTemperature(std::addressof(temp))); - - /* Set output. */ - *out_c = temp; - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryMaximumTemperature(float *out_c, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - u8 max_temp; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetMaximumTemperature(std::addressof(max_temp))); - - /* Set output. */ - *out_c = static_cast(max_temp); - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMinimumAlertThreshold(c)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMaximumAlertThreshold(c)); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryVCell(int *out_mv, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetVCell(out_mv)); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryAverageVCell(int *out_mv, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCell(out_mv)); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - double ms; - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCellTime(std::addressof(ms))); - - /* Set output. */ - *out = TimeSpan::FromMicroSeconds(static_cast(ms * 1000.0)); - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMinimumAlertThreshold(mv)); - - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Get the value. */ - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetOpenCircuitVoltage(out_mv)); - - return ResultSuccess(); - } - - Result BatteryDriver::SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMaximumAlertThreshold(mv)); - - return ResultSuccess(); - } - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp deleted file mode 100644 index 995b7dc..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "../../powctl_i_power_control_driver.hpp" -#include "powctl_interrupt_event_handler.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - class BatteryDevice : public powctl::impl::IDevice { - NON_COPYABLE(BatteryDevice); - NON_MOVEABLE(BatteryDevice); - AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDevice, ::ams::powctl::impl::IDevice); - private: - bool use_event_handler; - std::optional event_handler; - os::SystemEventType system_event; - public: - BatteryDevice(bool ev); - - os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } - - void SetInterruptEnabled(bool en) { - if (this->use_event_handler) { - this->event_handler->SetInterruptEnabled(en); - } - } - }; - - class BatteryDriver : public IPowerControlDriver { - NON_COPYABLE(BatteryDriver); - NON_MOVEABLE(BatteryDriver); - AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDriver, ::ams::powctl::impl::IPowerControlDriver); - public: - BatteryDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } - - /* Generic API. */ - virtual void InitializeDriver() override; - virtual void FinalizeDriver() override; - - virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; - virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; - - virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; - virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; - - /* Battery API. */ - virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override; - - virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override; - - virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override; - virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override; - - virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override; - virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override; - virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override; - - virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override; - virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override; - - virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override; - virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override; - - virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override; - virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override; - - virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override; - virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override; - - virtual Result IsBatteryPresent(bool *out, IDevice *device) override; - - virtual Result GetBatteryCycles(int *out, IDevice *device) override; - virtual Result SetBatteryCycles(IDevice *device, int cycles) override; - - virtual Result GetBatteryAge(float *out_percent, IDevice *device) override; - - virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override; - virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override; - - virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override; - virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override; - - virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override; - virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override; - - virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override; - - virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override; - - virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override; - - virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override; - - /* Unsupported Charger API. */ - virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } - - virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override { return powctl::ResultNotSupported(); } - - virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } - - virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } - - virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } - - virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override { return powctl::ResultNotSupported(); } - virtual Result ResetChargerWatchdogTimer(IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override { return powctl::ResultNotSupported(); } - - virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } - }; - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp deleted file mode 100644 index c22f7ed..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 "../../powctl_device_management.hpp" -#include "powctl_board_impl.hpp" -#include "powctl_battery_driver.hpp" -#include "powctl_charger_driver.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace { - - constinit std::optional g_charger_driver; - constinit std::optional g_battery_driver; - - void InitializeChargerDriver(bool use_event_handlers) { - /* Create the charger driver. */ - g_charger_driver.emplace(use_event_handlers); - - /* Register the driver. */ - powctl::impl::RegisterDriver(std::addressof(*g_charger_driver)); - } - - void InitializeBatteryDriver(bool use_event_handlers) { - /* Create the battery driver. */ - g_battery_driver.emplace(use_event_handlers); - - /* Register the driver. */ - powctl::impl::RegisterDriver(std::addressof(*g_battery_driver)); - } - - void FinalizeChargerDriver() { - /* Unregister the driver. */ - powctl::impl::UnregisterDriver(std::addressof(*g_charger_driver)); - - /* Destroy the battery driver. */ - g_charger_driver = std::nullopt; - } - - void FinalizeBatteryDriver() { - /* Unregister the driver. */ - powctl::impl::UnregisterDriver(std::addressof(*g_battery_driver)); - - /* Destroy the battery driver. */ - g_battery_driver = std::nullopt; - } - - } - - void Initialize(bool use_event_handlers) { - InitializeChargerDriver(use_event_handlers); - InitializeBatteryDriver(use_event_handlers); - } - - void Finalize() { - FinalizeBatteryDriver(); - FinalizeChargerDriver(); - } - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp deleted file mode 100644 index 902c5e9..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#include "powctl_interrupt_event_handler.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - void Initialize(bool use_event_handlers); - void Finalize(); - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp deleted file mode 100644 index 32fa8a9..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - * 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 "powctl_bq24193_driver.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace bq24193 { - - constexpr inline u8 InputSourceControl = 0x00; - constexpr inline u8 PowerOnConfiguration = 0x01; - constexpr inline u8 ChargeCurrentControl = 0x02; - constexpr inline u8 PreChargeTerminationCurrentControl = 0x03; - constexpr inline u8 ChargeVoltageControl = 0x04; - constexpr inline u8 ChargeTerminationTimerControl = 0x05; - constexpr inline u8 IrCompensationThermalRegulationControl = 0x06; - constexpr inline u8 MiscOperationControl = 0x07; - constexpr inline u8 SystemStatus = 0x08; - constexpr inline u8 Fault = 0x09; - constexpr inline u8 VendorPartRevisionStatus = 0x0A; - - constexpr u8 EncodePreChargeCurrentLimit(int ma) { - constexpr int Minimum = 128; - constexpr int Maximum = 2048; - ma = std::max(std::min(ma, Maximum), Minimum); - - return static_cast((static_cast(ma - Minimum) >> 7) << 4); - } - - constexpr u8 EncodeTerminationCurrentLimit(int ma) { - constexpr int Minimum = 128; - constexpr int Maximum = 2048; - ma = std::max(std::min(ma, Maximum), Minimum); - - return static_cast((static_cast(ma - Minimum) >> 7) << 0); - } - - constexpr u8 EncodeMinimumSystemVoltageLimit(int mv) { - constexpr int Minimum = 3000; - constexpr int Maximum = 3700; - mv = std::max(std::min(mv, Maximum), Minimum); - - return static_cast(((mv - Minimum) / 100) << 1); - } - - constexpr u8 EncodeFastChargeCurrentLimit(int ma) { - constexpr int Minimum = 512; - constexpr int Maximum = 4544; - ma = std::max(std::min(ma, Maximum), Minimum); - - return static_cast((static_cast(ma - Minimum) >> 6) << 2); - } - - constexpr int DecodeFastChargeCurrentLimit(u8 reg) { - constexpr int Minimum = 512; - - return Minimum + (static_cast(reg & 0xFC) << 4); - } - - static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(512)) == 512); - static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(4544)) == 4544); - static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(576)) == 576); - - constexpr u8 EncodeChargeVoltageLimit(int mv) { - constexpr int Minimum = 3504; - constexpr int Maximum = 4400; - mv = std::max(std::min(mv, Maximum), Minimum); - - return static_cast((static_cast(mv - Minimum) >> 4) << 2); - } - - constexpr int DecodeChargeVoltageLimit(u8 reg) { - constexpr int Minimum = 3504; - - return Minimum + (static_cast(reg & 0xFC) << 2); - } - - static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3504)) == 3504); - static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(4400)) == 4400); - static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3520)) == 3520); - - constexpr u8 EncodeChargerConfiguration(bq24193::ChargerConfiguration cfg) { - switch (cfg) { - case ChargerConfiguration_ChargeDisable: return 0x00; - case ChargerConfiguration_ChargeBattery: return 0x10; - case ChargerConfiguration_Otg: return 0x20; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - constexpr u8 EncodeWatchdogTimerSetting(int seconds) { - if (seconds == 0) { - return 0x00; - } else if (seconds < 80) { - return 0x10; - } else if (seconds < 160) { - return 0x20; - } else { - return 0x30; - } - } - - constexpr u8 EncodeBatteryCompensation(int mo) { - constexpr int Minimum = 0; - constexpr int Maximum = 70; - mo = std::max(std::min(mo, Maximum), Minimum); - - return static_cast((static_cast(mo - Minimum) / 10) << 5); - } - - constexpr int DecodeBatteryCompensation(u8 reg) { - constexpr int Minimum = 0; - - return Minimum + (static_cast(reg & 0xE0) >> 5) * 10; - } - - static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(0)) == 0); - static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(70)) == 70); - static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(30)) == 30); - - constexpr u8 EncodeVoltageClamp(int mv) { - constexpr int Minimum = 0; - constexpr int Maximum = 112; - mv = std::max(std::min(mv, Maximum), Minimum); - - return static_cast((static_cast(mv - Minimum) >> 4) << 2); - } - - constexpr int DecodeVoltageClamp(u8 reg) { - constexpr int Minimum = 0; - - return Minimum + (static_cast(reg & 0x1C) << 2); - } - - static_assert(DecodeVoltageClamp(EncodeVoltageClamp(0)) == 0); - static_assert(DecodeVoltageClamp(EncodeVoltageClamp(112)) == 112); - static_assert(DecodeVoltageClamp(EncodeVoltageClamp(64)) == 64); - - constexpr u8 EncodeInputCurrentLimit(int ma) { - if (ma < 150) { - return 0; - } else if (ma < 500) { - return 1; - } else if (ma < 900) { - return 2; - } else if (ma < 1200) { - return 3; - } else if (ma < 1500) { - return 4; - } else if (ma < 2000) { - return 5; - } else if (ma < 3000) { - return 6; - } else{ - return 7; - } - } - - constexpr int DecodeInputCurrentLimit(u8 reg) { - switch (reg & 0x07) { - case 0: return 100; - case 1: return 150; - case 2: return 500; - case 3: return 900; - case 4: return 1200; - case 5: return 1500; - case 6: return 2000; - case 7: return 3000; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(100)) == 100); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(150)) == 150); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(500)) == 500); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(900)) == 900); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1200)) == 1200); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1500)) == 1500); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(2000)) == 2000); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(3000)) == 3000); - - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(0)) == 100); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(9999)) == 3000); - - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(149)) == 100); - static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(151)) == 150); - - constexpr u8 EncodeInputVoltageLimit(int mv) { - constexpr int Minimum = 3880; - constexpr int Maximum = 5080; - mv = std::max(std::min(mv, Maximum), Minimum); - - return static_cast(((static_cast(mv - Minimum) / 80) & 0xF) << 3); - } - - constexpr u8 EncodeBoostModeCurrentLimit(int ma) { - return ma >= 1300 ? 1 : 0; - } - - constexpr bq24193::ChargerStatus DecodeChargerStatus(u8 reg) { - switch (reg & 0x30) { - case 0x00: return bq24193::ChargerStatus_NotCharging; - case 0x10: return bq24193::ChargerStatus_PreCharge; - case 0x20: return bq24193::ChargerStatus_FastCharging; - case 0x30: return bq24193::ChargerStatus_ChargeTerminationDone; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - } - - namespace { - - ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u8 mask, u8 value) { - /* Read the current value. */ - u8 cur_val; - R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); - - /* Update the value. */ - const u8 new_val = (cur_val & ~mask) | (value & mask); - R_TRY(i2c::WriteSingleRegister(session, address, new_val)); - - return ResultSuccess(); - } - - } - - Result Bq24193Driver::InitializeSession() { - /* Set fast charge current limit. */ - R_TRY(this->SetFastChargeCurrentLimit(512)); - - /* Disable force 20 percent charge. */ - R_TRY(this->SetForce20PercentChargeCurrent(false)); - - /* Set pre-charge current limit. */ - R_TRY(this->SetPreChargeCurrentLimit(128)); - - /* Set termination current limit. */ - R_TRY(this->SetTerminationCurrentLimit(128)); - - /* Set minimum system voltage limit. */ - R_TRY(this->SetMinimumSystemVoltageLimit(3000)); - - /* Set watchdog timer setting. */ - R_TRY(this->SetWatchdogTimerSetting(0)); - - /* Disable charging safety timer. */ - R_TRY(this->SetChargingSafetyTimerEnabled(false)); - - /* Reset the watchdog timer. */ - R_TRY(this->ResetWatchdogTimer()); - - return ResultSuccess(); - } - - Result Bq24193Driver::SetPreChargeCurrentLimit(int ma) { - return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(ma)); - } - - Result Bq24193Driver::SetTerminationCurrentLimit(int ma) { - return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(ma)); - } - - Result Bq24193Driver::SetMinimumSystemVoltageLimit(int mv) { - return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(mv)); - } - - Result Bq24193Driver::SetChargingSafetyTimerEnabled(bool en) { - return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x08, en ? 0x08 : 0x00); - } - - Result Bq24193Driver::GetForce20PercentChargeCurrent(bool *out) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); - - /* Extract the value. */ - *out = (val & 0x01) != 0; - return ResultSuccess(); - } - - Result Bq24193Driver::SetForce20PercentChargeCurrent(bool en) { - return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0x01, en ? 0x01 : 0x00); - } - - Result Bq24193Driver::GetFastChargeCurrentLimit(int *out_ma) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); - - /* Extract the value. */ - *out_ma = bq24193::DecodeFastChargeCurrentLimit(val); - return ResultSuccess(); - } - - Result Bq24193Driver::SetFastChargeCurrentLimit(int ma) { - return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(ma)); - } - - Result Bq24193Driver::GetChargeVoltageLimit(int *out_mv) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeVoltageControl, std::addressof(val))); - - /* Extract the value. */ - *out_mv = bq24193::DecodeChargeVoltageLimit(val); - return ResultSuccess(); - } - - Result Bq24193Driver::SetChargeVoltageLimit(int mv) { - return ReadWriteRegister(this->i2c_session, bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(mv)); - } - - Result Bq24193Driver::SetChargerConfiguration(bq24193::ChargerConfiguration cfg) { - return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x30, bq24193::EncodeChargerConfiguration(cfg)); - } - - Result Bq24193Driver::IsHiZEnabled(bool *out) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); - - /* Extract the value. */ - *out = (val & 0x80) != 0; - return ResultSuccess(); - } - - Result Bq24193Driver::SetHiZEnabled(bool en) { - return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x80, en ? 0x80 : 0x00); - } - - Result Bq24193Driver::GetInputCurrentLimit(int *out_ma) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); - - /* Extract the value. */ - *out_ma = bq24193::DecodeInputCurrentLimit(val); - return ResultSuccess(); - } - - Result Bq24193Driver::SetInputCurrentLimit(int ma) { - return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x07, bq24193::EncodeInputCurrentLimit(ma)); - } - - Result Bq24193Driver::SetInputVoltageLimit(int mv) { - return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x78, bq24193::EncodeInputVoltageLimit(mv)); - } - - Result Bq24193Driver::SetBoostModeCurrentLimit(int ma) { - return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x01, bq24193::EncodeBoostModeCurrentLimit(ma)); - } - - Result Bq24193Driver::GetChargerStatus(bq24193::ChargerStatus *out) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::SystemStatus, std::addressof(val))); - - /* Extract the value. */ - *out = bq24193::DecodeChargerStatus(val); - return ResultSuccess(); - } - - Result Bq24193Driver::ResetWatchdogTimer() { - return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x40, 0x40); - } - - Result Bq24193Driver::SetWatchdogTimerSetting(int seconds) { - return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x30, bq24193::EncodeWatchdogTimerSetting(seconds)); - } - - Result Bq24193Driver::GetBatteryCompensation(int *out_mo) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); - - /* Extract the value. */ - *out_mo = bq24193::DecodeBatteryCompensation(val); - return ResultSuccess(); - } - - Result Bq24193Driver::SetBatteryCompensation(int mo) { - return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0xE0, bq24193::EncodeBatteryCompensation(mo)); - } - - Result Bq24193Driver::GetVoltageClamp(int *out_mv) { - /* Get the register. */ - u8 val; - R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); - - /* Extract the value. */ - *out_mv = bq24193::DecodeVoltageClamp(val); - return ResultSuccess(); - } - - Result Bq24193Driver::SetVoltageClamp(int mv) { - return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0x1C, bq24193::EncodeVoltageClamp(mv)); - } - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp deleted file mode 100644 index 01d9b90..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace bq24193 { - - enum ChargerConfiguration { - ChargerConfiguration_ChargeDisable = 0, - ChargerConfiguration_ChargeBattery = 1, - ChargerConfiguration_Otg = 2, - }; - - enum ChargerStatus { - ChargerStatus_NotCharging = 0, - ChargerStatus_PreCharge = 1, - ChargerStatus_FastCharging = 2, - ChargerStatus_ChargeTerminationDone = 3, - }; - - } - - class Bq24193Driver { - private: - os::SdkMutex mutex; - int init_count; - i2c::I2cSession i2c_session; - private: - Result InitializeSession(); - public: - Bq24193Driver() : mutex(), init_count(0), i2c_session() { - /* ... */ - } - - void Initialize() { - std::scoped_lock lk(this->mutex); - if ((this->init_count++) == 0) { - /* Initialize i2c library. */ - i2c::InitializeEmpty(); - - /* Open session. */ - R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193)); - - /* Initialize session. */ - R_ABORT_UNLESS(this->InitializeSession()); - } - } - - void Finalize() { - std::scoped_lock lk(this->mutex); - if ((--this->init_count) == 0) { - /* Close session. */ - i2c::CloseSession(this->i2c_session); - - /* Finalize i2c library. */ - i2c::Finalize(); - } - } - - Result SetPreChargeCurrentLimit(int ma); - Result SetTerminationCurrentLimit(int ma); - - Result SetMinimumSystemVoltageLimit(int mv); - - Result SetChargingSafetyTimerEnabled(bool en); - - Result GetForce20PercentChargeCurrent(bool *out); - Result SetForce20PercentChargeCurrent(bool en); - - Result GetFastChargeCurrentLimit(int *out_ma); - Result SetFastChargeCurrentLimit(int ma); - - Result GetChargeVoltageLimit(int *out_mv); - Result SetChargeVoltageLimit(int mv); - - Result SetChargerConfiguration(bq24193::ChargerConfiguration cfg); - - Result IsHiZEnabled(bool *out); - Result SetHiZEnabled(bool en); - - Result GetInputCurrentLimit(int *out_ma); - Result SetInputCurrentLimit(int ma); - - Result SetInputVoltageLimit(int mv); - - Result SetBoostModeCurrentLimit(int ma); - - Result GetChargerStatus(bq24193::ChargerStatus *out); - - Result ResetWatchdogTimer(); - Result SetWatchdogTimerSetting(int seconds); - - Result GetBatteryCompensation(int *out_mo); - Result SetBatteryCompensation(int mo); - - Result GetVoltageClamp(int *out_mv); - Result SetVoltageClamp(int mv); - }; - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp deleted file mode 100644 index 3771bbb..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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 "../../powctl_device_management.hpp" -#include "powctl_retry_helper.hpp" -#include "powctl_charger_driver.hpp" -#include "powctl_bq24193_driver.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace { - - constinit std::optional g_charger_device; - - Bq24193Driver &GetBq24193Driver() { - static Bq24193Driver s_bq24193_driver; - return s_bq24193_driver; - } - - } - - ChargerDevice::ChargerDevice(bool ev) : gpio_pad_session(), watchdog_timer_enabled(false), watchdog_timer_timeout(0), use_event_handler(ev), event_handler() { - if (this->use_event_handler) { - /* Create the system event. */ - os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); - - /* Create the handler. */ - this->event_handler.emplace(this); - - /* Register the event handler. */ - powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); - } - } - - /* Generic API. */ - void ChargerDriver::InitializeDriver() { - /* Initialize Bq24193Driver */ - GetBq24193Driver().Initialize(); - - /* Initialize gpio library. */ - gpio::Initialize(); - - /* Create charger device. */ - g_charger_device.emplace(this->IsEventHandlerEnabled()); - - /* Open the device's gpio session. */ - R_ABORT_UNLESS(gpio::OpenSession(g_charger_device->GetPadSession(), gpio::DeviceCode_BattChgEnableN)); - - /* Configure the gpio session as output. */ - gpio::SetDirection(g_charger_device->GetPadSession(), gpio::Direction_Output); - - /* Register our device. */ - this->RegisterDevice(std::addressof(*g_charger_device)); - - /* Register the charger device's code. */ - R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Bq24193, std::addressof(*g_charger_device))); - } - - void ChargerDriver::FinalizeDriver() { - /* Unregister the charger device code. */ - powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Bq24193); - - /* Unregister our device. */ - this->UnregisterDevice(std::addressof(*g_charger_device)); - - /* Close the device's gpio session. */ - gpio::CloseSession(g_charger_device->GetPadSession()); - - /* Destroy the charger device. */ - g_charger_device = std::nullopt; - - /* Finalize gpio library. */ - gpio::Finalize(); - - /* Finalize Bq24193Driver. */ - GetBq24193Driver().Finalize(); - } - - Result ChargerDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Check that we support event handlers. */ - R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); - - *out = device->SafeCastTo().GetSystemEvent(); - return ResultSuccess(); - } - - Result ChargerDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Set the interrupt enable. */ - device->SafeCastTo().SetInterruptEnabled(enable); - - return ResultSuccess(); - } - - Result ChargerDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); - } - - Result ChargerDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { - /* TODO */ - AMS_ABORT(); - } - - /* Charger API. */ - Result ChargerDriver::GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - /* Check if we're not charging. */ - if (gpio::GetValue(device->SafeCastTo().GetPadSession()) == gpio::GpioValue_High) { - *out = ChargeCurrentState_NotCharging; - } else { - /* Get force 20 percent charge state. */ - bool force_20_percent; - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetForce20PercentChargeCurrent(std::addressof(force_20_percent))); - - /* Set output appropriately. */ - if (force_20_percent) { - *out = ChargeCurrentState_ChargingForce20Percent; - } else { - *out = ChargeCurrentState_Charging; - } - } - - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - switch (state) { - case ChargeCurrentState_NotCharging: - gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_High); - break; - case ChargeCurrentState_ChargingForce20Percent: - case ChargeCurrentState_Charging: - gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_Low); - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetForce20PercentChargeCurrent(state == ChargeCurrentState_ChargingForce20Percent)); - break; - case ChargeCurrentState_Unknown: - return powctl::ResultInvalidArgument(); - } - - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetFastChargeCurrentLimit(out_ma)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerFastChargeCurrentLimit(IDevice *device, int ma) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetFastChargeCurrentLimit(ma)); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargeVoltageLimit(out_mv)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerChargeVoltageLimit(IDevice *device, int mv) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargeVoltageLimit(mv)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - bq24193::ChargerConfiguration bq_cfg; - switch (cfg) { - case ChargerConfiguration_ChargeDisable: bq_cfg = bq24193::ChargerConfiguration_ChargeDisable; break; - case ChargerConfiguration_ChargeBattery: bq_cfg = bq24193::ChargerConfiguration_ChargeBattery; break; - case ChargerConfiguration_Otg: bq_cfg = bq24193::ChargerConfiguration_Otg; break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargerConfiguration(bq_cfg)); - return ResultSuccess(); - } - - Result ChargerDriver::IsChargerHiZEnabled(bool *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().IsHiZEnabled(out)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerHiZEnabled(IDevice *device, bool en) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetHiZEnabled(en)); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerInputCurrentLimit(int *out_ma, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetInputCurrentLimit(out_ma)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerInputCurrentLimit(IDevice *device, int ma) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputCurrentLimit(ma)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerInputVoltageLimit(IDevice *device, int mv) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputVoltageLimit(mv)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerBoostModeCurrentLimit(IDevice *device, int ma) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBoostModeCurrentLimit(ma)); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerChargerStatus(ChargerStatus *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - bq24193::ChargerStatus bq_status; - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargerStatus(std::addressof(bq_status))); - - switch (bq_status) { - case bq24193::ChargerStatus_NotCharging: - *out = ChargerStatus_NotCharging; - break; - case bq24193::ChargerStatus_PreCharge: - case bq24193::ChargerStatus_FastCharging: - *out = ChargerStatus_Charging; - break; - case bq24193::ChargerStatus_ChargeTerminationDone: - *out = ChargerStatus_ChargeTerminationDone; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - return ResultSuccess(); - } - - Result ChargerDriver::IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - *out = device->SafeCastTo().IsWatchdogTimerEnabled(); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerWatchdogTimerEnabled(IDevice *device, bool en) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - auto &charger_device = device->SafeCastTo(); - - if (en) { - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(charger_device.GetWatchdogTimerTimeout().GetSeconds())); - } else { - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(0)); - } - - charger_device.SetWatchdogTimerEnabled(en); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - device->SafeCastTo().SetWatchdogTimerTimeout(timeout); - return ResultSuccess(); - } - - Result ChargerDriver::ResetChargerWatchdogTimer(IDevice *device) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerBatteryCompensation(int *out_mo, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mo != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetBatteryCompensation(out_mo)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerBatteryCompensation(IDevice *device, int mo) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBatteryCompensation(mo)); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargerVoltageClamp(int *out_mv, IDevice *device) { - /* Validate arguments. */ - R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetVoltageClamp(out_mv)); - return ResultSuccess(); - } - - Result ChargerDriver::SetChargerVoltageClamp(IDevice *device, int mv) { - /* Validate arguments. */ - R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); - - AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetVoltageClamp(mv)); - return ResultSuccess(); - } - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp deleted file mode 100644 index 9b133e1..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "../../powctl_i_power_control_driver.hpp" -#include "powctl_interrupt_event_handler.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - class ChargerDevice : public powctl::impl::IDevice { - NON_COPYABLE(ChargerDevice); - NON_MOVEABLE(ChargerDevice); - AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDevice, ::ams::powctl::impl::IDevice); - private: - gpio::GpioPadSession gpio_pad_session; - bool watchdog_timer_enabled; - TimeSpan watchdog_timer_timeout; - bool use_event_handler; - std::optional event_handler; - os::SystemEventType system_event; - public: - ChargerDevice(bool ev); - - bool IsWatchdogTimerEnabled() const { return this->watchdog_timer_enabled; } - void SetWatchdogTimerEnabled(bool en) { this->watchdog_timer_enabled = en; } - - TimeSpan GetWatchdogTimerTimeout() const { return this->watchdog_timer_timeout; } - void SetWatchdogTimerTimeout(TimeSpan ts) { this->watchdog_timer_timeout = ts; } - - gpio::GpioPadSession *GetPadSession() { return std::addressof(this->gpio_pad_session); } - - os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } - - void SetInterruptEnabled(bool en) { - if (this->use_event_handler) { - this->event_handler->SetInterruptEnabled(en); - } - } - }; - - class ChargerDriver : public IPowerControlDriver { - NON_COPYABLE(ChargerDriver); - NON_MOVEABLE(ChargerDriver); - AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDriver, ::ams::powctl::impl::IPowerControlDriver); - public: - ChargerDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } - - /* Generic API. */ - virtual void InitializeDriver() override; - virtual void FinalizeDriver() override; - - virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; - virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; - - virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; - virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; - - /* Charger API. */ - virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override; - virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override; - - virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override; - virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override; - - virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override; - virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override; - - virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override; - - virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override; - virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override; - - virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override; - virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override; - - virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override; - - virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override; - - virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override; - - virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override; - virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override; - - virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override; - virtual Result ResetChargerWatchdogTimer(IDevice *device) override; - - virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override; - virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override; - - virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override; - virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override; - - /* Unsupported Battery API. */ - virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } - - virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } - - virtual Result IsBatteryPresent(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryCycles(int *out, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryCycles(IDevice *device, int cycles) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryAge(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } - virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } - virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } - - virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } - - virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } - }; - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp deleted file mode 100644 index 360abec..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 "powctl_interrupt_event_handler.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - void ChargerInterruptEventHandler::SignalEvent(IDevice *device) { - /* TODO */ - AMS_ABORT(); - } - - void BatteryInterruptEventHandler::SignalEvent(IDevice *device) { - /* TODO */ - AMS_ABORT(); - } - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp deleted file mode 100644 index 40be3dc..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "../../powctl_i_power_control_driver.hpp" - -namespace ams::powctl::impl::board::nintendo_nx { - - template - class InterruptEventHandler : public ddsf::IEventHandler { - private: - IDevice *device; - gpio::GpioPadSession gpio_session; - os::SystemEventType gpio_system_event; - os::SdkMutex mutex; - public: - InterruptEventHandler(IDevice *dv) : IEventHandler(), device(dv), mutex() { - /* Initialize the gpio session. */ - Derived::Initialize(std::addressof(this->gpio_session), std::addressof(this->gpio_system_event)); - - /* Initialize ourselves as an event handler. */ - IEventHandler::Initialize(std::addressof(this->gpio_system_event)); - } - - os::SystemEventType *GetSystemEvent() { - return std::addressof(this->gpio_system_event); - } - - void SetInterruptEnabled(bool en) { - std::scoped_lock lk(this->mutex); - - gpio::SetInterruptEnable(std::addressof(this->gpio_session), en); - } - - virtual void HandleEvent() override final { - /* Acquire exclusive access to ourselves. */ - std::scoped_lock lk(this->mutex); - - /* Clear our interrupt status. */ - gpio::ClearInterruptStatus(std::addressof(this->gpio_session)); - - /* Clear our system event. */ - os::ClearSystemEvent(std::addressof(this->gpio_system_event)); - - /* Signal the event. */ - static_cast(this)->SignalEvent(this->device); - } - }; - - class ChargerInterruptEventHandler : public InterruptEventHandler { - friend class InterruptEventHandler; - private: - static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { - /* Open the gpio session. */ - R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_Bq24190Irq)); - - /* Configure the gpio session. */ - gpio::SetDirection(session, gpio::Direction_Input); - gpio::SetInterruptMode(session, gpio::InterruptMode_FallingEdge); - gpio::SetInterruptEnable(session, true); - - /* Bind the interrupt event. */ - R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); - } - - void SignalEvent(IDevice *device); - public: - ChargerInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } - }; - - class BatteryInterruptEventHandler : public InterruptEventHandler { - friend class InterruptEventHandler; - private: - static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { - /* Open the gpio session. */ - R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_BattMgicIrq)); - - /* Configure the gpio session. */ - gpio::SetDirection(session, gpio::Direction_Input); - gpio::SetInterruptMode(session, gpio::InterruptMode_LowLevel); - - /* Bind the interrupt event. */ - R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); - } - - void SignalEvent(IDevice *device); - public: - BatteryInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } - }; - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc deleted file mode 100644 index 03c5a02..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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 . - */ - -/* NOTE: This file is auto-generated by max17050_parameters_gen.py, do not edit manually. */ - -constexpr inline const CustomParameters CustomParameters0A = { - .relaxcfg = 0x203B, - .rcomp0 = 0x0053, - .tempco = 0x1C22, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x5786, - .qresidual10 = 0x3184, - .qresidual20 = 0x1E00, - .qresidual30 = 0x1502, - .fullcap = 0x2476, - .vffullcap = 0x2476, - .modeltbl = { - 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, - 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, - 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, - 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5F00, - .iavgempty = 0x1D2A, -}; - -constexpr inline const CustomParameters CustomParameters0R = { - .relaxcfg = 0x203B, - .rcomp0 = 0x0048, - .tempco = 0x2034, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x5A00, - .qresidual10 = 0x3B00, - .qresidual20 = 0x0F80, - .qresidual30 = 0x0B02, - .fullcap = 0x2466, - .vffullcap = 0x2466, - .modeltbl = { - 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, - 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, - 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, - 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5F00, - .iavgempty = 0x1D2A, -}; - -constexpr inline const CustomParameters CustomParameters0M = { - .relaxcfg = 0x203B, - .rcomp0 = 0x0085, - .tempco = 0x1625, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x3100, - .qresidual10 = 0x1B00, - .qresidual20 = 0x1000, - .qresidual30 = 0x0C81, - .fullcap = 0x227A, - .vffullcap = 0x227A, - .modeltbl = { - 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, - 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, - 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, - 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, - 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, - 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, - }, - .fullsocthr = 0x5F00, - .iavgempty = 0x1D2A, -}; - -constexpr inline const CustomParameters CustomParameters1 = { - .relaxcfg = 0x203B, - .rcomp0 = 0x0040, - .tempco = 0x1624, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x4690, - .qresidual10 = 0x2605, - .qresidual20 = 0x1605, - .qresidual30 = 0x0F05, - .fullcap = 0x1AE4, - .vffullcap = 0x1AE4, - .modeltbl = { - 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, - 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, - 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, - 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5F00, - .iavgempty = 0x1584, -}; - -constexpr inline const CustomParameters CustomParameters2A = { - .relaxcfg = 0x203B, - .rcomp0 = 0x004A, - .tempco = 0x1D23, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x4000, - .qresidual10 = 0x1E80, - .qresidual20 = 0x0D83, - .qresidual30 = 0x0783, - .fullcap = 0x1C20, - .vffullcap = 0x1C20, - .modeltbl = { - 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, - 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, - 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, - 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5500, - .iavgempty = 0x1680, -}; - -constexpr inline const CustomParameters CustomParameters2R = { - .relaxcfg = 0x203B, - .rcomp0 = 0x004C, - .tempco = 0x2D32, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x5900, - .qresidual10 = 0x2900, - .qresidual20 = 0x1100, - .qresidual30 = 0x0B00, - .fullcap = 0x1CCE, - .vffullcap = 0x1CCE, - .modeltbl = { - 0x8E10, 0x9FC0, 0xA880, 0xB750, 0xBA10, 0xBB30, 0xBD20, 0xBE80, - 0xC0A0, 0xC350, 0xC670, 0xC8C0, 0xCCF0, 0xD050, 0xD140, 0xD5F0, - 0x0020, 0x00D0, 0x0200, 0x0E00, 0x1300, 0x1B00, 0x1930, 0x1150, - 0x0BF0, 0x07E0, 0x0AD0, 0x06F0, 0x07F0, 0x0EF0, 0x04F0, 0x04F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5500, - .iavgempty = 0x170B, -}; - -constexpr inline const CustomParameters CustomParameters2M = { - .relaxcfg = 0x203B, - .rcomp0 = 0x0049, - .tempco = 0x222A, - .ichgterm = 0x0333, - .tgain = 0xE1F6, - .toff = 0x2BF2, - .vempty = 0xA05F, - .qresidual00 = 0x4F00, - .qresidual10 = 0x2680, - .qresidual20 = 0x1205, - .qresidual30 = 0x0C87, - .fullcap = 0x1C68, - .vffullcap = 0x1C68, - .modeltbl = { - 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, - 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, - 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, - 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - .fullsocthr = 0x5500, - .iavgempty = 0x16B9, -}; - diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp deleted file mode 100644 index 424c65b..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/* - * 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 "powctl_max17050_driver.hpp" - -#if defined(ATMOSPHERE_ARCH_ARM64) -#include -#endif - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace max17050 { - - constexpr inline u8 Status = 0x00; - constexpr inline u8 VAlrtThreshold = 0x01; - constexpr inline u8 TAlrtThreshold = 0x02; - constexpr inline u8 SocAlrtThreshold = 0x03; - constexpr inline u8 AtRate = 0x04; - constexpr inline u8 RemCapRep = 0x05; - constexpr inline u8 SocRep = 0x06; - constexpr inline u8 Age = 0x07; - constexpr inline u8 Temperature = 0x08; - constexpr inline u8 VCell = 0x09; - constexpr inline u8 Current = 0x0A; - constexpr inline u8 AverageCurrent = 0x0B; - - constexpr inline u8 SocMix = 0x0D; - constexpr inline u8 SocAv = 0x0E; - constexpr inline u8 RemCapMix = 0x0F; - constexpr inline u8 FullCap = 0x10; - constexpr inline u8 Tte = 0x11; - constexpr inline u8 QResidual00 = 0x12; - constexpr inline u8 FullSocThr = 0x13; - - - constexpr inline u8 AverageTemp = 0x16; - constexpr inline u8 Cycles = 0x17; - constexpr inline u8 DesignCap = 0x18; - constexpr inline u8 AverageVCell = 0x19; - constexpr inline u8 MaxMinTemp = 0x1A; - constexpr inline u8 MaxMinVoltage = 0x1B; - constexpr inline u8 MaxMinCurrent = 0x1C; - constexpr inline u8 Config = 0x1D; - constexpr inline u8 IChgTerm = 0x1E; - constexpr inline u8 RemCapAv = 0x1F; - - constexpr inline u8 Version = 0x21; - constexpr inline u8 QResidual10 = 0x22; - constexpr inline u8 FullCapNom = 0x23; - constexpr inline u8 TempNom = 0x24; - constexpr inline u8 TempLim = 0x25; - - constexpr inline u8 Ain = 0x27; - constexpr inline u8 LearnCfg = 0x28; - constexpr inline u8 FilterCfg = 0x29; - constexpr inline u8 RelaxCfg = 0x2A; - constexpr inline u8 MiscCfg = 0x2B; - constexpr inline u8 TGain = 0x2C; - constexpr inline u8 TOff = 0x2D; - constexpr inline u8 CGain = 0x2E; - constexpr inline u8 COff = 0x2F; - - - constexpr inline u8 QResidual20 = 0x32; - - - constexpr inline u8 FullCap0 = 0x35; - constexpr inline u8 IAvgEmpty = 0x36; - constexpr inline u8 FCtc = 0x37; - constexpr inline u8 RComp0 = 0x38; - constexpr inline u8 TempCo = 0x39; - constexpr inline u8 VEmpty = 0x3A; - - - constexpr inline u8 FStat = 0x3D; - constexpr inline u8 Timer = 0x3E; - constexpr inline u8 ShdnTimer = 0x3F; - - - constexpr inline u8 QResidual30 = 0x42; - - - constexpr inline u8 DQAcc = 0x45; - constexpr inline u8 DPAcc = 0x46; - - constexpr inline u8 SocVf0 = 0x48; - - constexpr inline u8 Qh0 = 0x4C; - constexpr inline u8 Qh = 0x4D; - - constexpr inline u8 SocVfAccess = 0x60; - - constexpr inline u8 ModelAccess0 = 0x62; - constexpr inline u8 ModelAccess1 = 0x63; - - constexpr inline u8 ModelChrTblStart = 0x80; - constexpr inline u8 ModelChrTblEnd = 0xB0; - - - constexpr inline u8 VFocV = 0xFB; - constexpr inline u8 SocVf = 0xFF; - - constexpr inline size_t ModelChrTblSize = ModelChrTblEnd - ModelChrTblStart; - - namespace { - - struct CustomParameters { - u16 relaxcfg; - u16 rcomp0; - u16 tempco; - u16 ichgterm; - u16 tgain; - u16 toff; - u16 vempty; - u16 qresidual00; - u16 qresidual10; - u16 qresidual20; - u16 qresidual30; - u16 fullcap; - u16 vffullcap; - u16 modeltbl[ModelChrTblSize]; - u16 fullsocthr; - u16 iavgempty; - }; - - #include "powctl_max17050_custom_parameters.inc" - - const CustomParameters &GetCustomParameters(const char *battery_vendor, u8 battery_version) { - if (battery_version == 2) { - if (battery_vendor[7] == 'M') { - return CustomParameters2M; - } else if (battery_vendor[7] == 'R') { - return CustomParameters2R; - } else /* if (battery_vendor[7] == 'A') */ { - return CustomParameters2A; - } - } else if (battery_version == 1) { - return CustomParameters1; - } else /* if (battery_version == 0) */ { - if (battery_vendor[7] == 'M') { - return CustomParameters0M; - } else if (battery_vendor[7] == 'R') { - return CustomParameters0R; - } else /* if (battery_vendor[7] == 'A') */ { - return CustomParameters0A; - } - } - } - - } - - } - - namespace { - - ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { - /* Read the current value. */ - u16 cur_val; - R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); - - /* Update the value. */ - const u16 new_val = (cur_val & ~mask) | (value & mask); - R_TRY(i2c::WriteSingleRegister(session, address, new_val)); - - return ResultSuccess(); - } - - ALWAYS_INLINE Result ReadRegister(const i2c::I2cSession &session, u8 address, u16 *out) { - return i2c::ReadSingleRegister(session, address, out); - } - - ALWAYS_INLINE Result WriteRegister(const i2c::I2cSession &session, u8 address, u16 val) { - return i2c::WriteSingleRegister(session, address, val); - } - - ALWAYS_INLINE bool WriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 val) { - /* Write the value. */ - R_ABORT_UNLESS(WriteRegister(session, address, val)); - - /* Give it time to take. */ - os::SleepThread(TimeSpan::FromMilliSeconds(3)); - - /* Read it back. */ - u16 new_val; - R_ABORT_UNLESS(ReadRegister(session, address, std::addressof(new_val))); - - return new_val == val; - } - - ALWAYS_INLINE Result ReadWriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { - /* Read the current value. */ - u16 cur_val; - R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); - - /* Update the value. */ - const u16 new_val = (cur_val & ~mask) | (value & mask); - while (!WriteValidateRegister(session, address, new_val)) { /* ... */ } - - return ResultSuccess(); - } - - double CoerceToDouble(u64 value) { - static_assert(sizeof(value) == sizeof(double)); - - double d; - __builtin_memcpy(std::addressof(d), std::addressof(value), sizeof(d)); - return d; - } - - double ExponentiateTwoToPower(s16 exponent, double scale) { - if (exponent >= 1024) { - exponent = exponent - 1023; - scale = scale * 8.98846567e307; - if (exponent >= 1024) { - exponent = std::min(exponent, 2046) - 1023; - scale = scale * 8.98846567e307; - } - } else if (exponent <= -1023) { - exponent = exponent + 969; - scale = scale * 2.00416836e-292; - if (exponent <= -1023) { - exponent = std::max(exponent, -1991) + 969; - scale = scale * 2.00416836e-292; - } - } - return scale * CoerceToDouble(static_cast(exponent + 1023) << 52); - } - - } - - Result Max17050Driver::InitializeSession(const char *battery_vendor, u8 battery_version) { - /* Get the custom parameters. */ - const auto ¶ms = max17050::GetCustomParameters(battery_vendor, battery_version); - - /* We only want to write the parameters on power on reset. */ - R_SUCCEED_IF(!this->IsPowerOnReset()); - - /* Set that we need to restore parameters. */ - R_TRY(this->SetNeedToRestoreParameters(true)); - - /* Wait for our configuration to take. */ - os::SleepThread(TimeSpan::FromMilliSeconds(500)); - - /* Write initial config. */ - R_TRY(WriteRegister(this->i2c_session, max17050::Config, 0x7210)); - - /* Write initial filter config. */ - R_TRY(WriteRegister(this->i2c_session, max17050::FilterCfg, 0x8784)); - - /* Write relax config. */ - R_TRY(WriteRegister(this->i2c_session, max17050::RelaxCfg, params.relaxcfg)); - - /* Write initial learn config. */ - R_TRY(WriteRegister(this->i2c_session, max17050::LearnCfg, 0x2603)); - - /* Write fullsocthr. */ - R_TRY(WriteRegister(this->i2c_session, max17050::FullSocThr, params.fullsocthr)); - - /* Write iavgempty. */ - R_TRY(WriteRegister(this->i2c_session, max17050::IAvgEmpty, params.iavgempty)); - - /* Unlock model table, write model table. */ - do { - R_TRY(this->UnlockModelTable()); - R_TRY(this->SetModelTable(params.modeltbl)); - } while (!this->IsModelTableSet(params.modeltbl)); - - /* Lock the model table, trying up to ten times. */ - { - size_t i = 0; - while (true) { - ++i; - - R_TRY(this->LockModelTable()); - - if (this->IsModelTableLocked()) { - break; - } - - R_SUCCEED_IF(i >= 10); - } - } - - /* Write and validate rcomp0 */ - while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, params.rcomp0)) { /* ... */ } - - /* Write and validate tempco */ - while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, params.tempco)) { /* ... */ } - - /* Write ichgterm. */ - R_TRY(WriteRegister(this->i2c_session, max17050::IChgTerm, params.ichgterm)); - - /* Write tgain. */ - R_TRY(WriteRegister(this->i2c_session, max17050::TGain, params.tgain)); - - /* Write toff. */ - R_TRY(WriteRegister(this->i2c_session, max17050::TOff, params.toff)); - - /* Write and validate vempty. */ - while (!WriteValidateRegister(this->i2c_session, max17050::VEmpty, params.vempty)) { /* ... */ } - - /* Write and validate qresidual. */ - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, params.qresidual00)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, params.qresidual10)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, params.qresidual20)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, params.qresidual30)) { /* ... */ } - - /* Write capacity parameters. */ - while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } - R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); - while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } - - /* Give some time for configuration to take. */ - os::SleepThread(TimeSpan::FromMilliSeconds(350)); - - /* Write vfsoc to vfsoc0, qh, to qh0. */ - u16 vfsoc, qh; - { - R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(vfsoc))); - R_TRY(this->UnlockVfSoc()); - while (!WriteValidateRegister(this->i2c_session, max17050::SocVf0, vfsoc)) { /* ... */ } - R_TRY(ReadRegister(this->i2c_session, max17050::Qh, std::addressof(qh))); - R_TRY(WriteRegister(this->i2c_session, max17050::Qh0, qh)); - R_TRY(this->LockVfSoc()); - } - - /* Reset cycles. */ - while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, 0x0060)) { /* ... */ } - - /* Load new capacity parameters. */ - const u16 remcap = static_cast((vfsoc * params.vffullcap) / 0x6400); - const u16 repcap = static_cast(remcap * (params.fullcap / params.vffullcap)); - const u16 dpacc = 0x0C80; - const u16 dqacc = params.vffullcap / 0x10; - while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, remcap)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::RemCapRep, repcap)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, dpacc)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, dqacc)) { /* ... */ } - - /* Write capacity parameters. */ - while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } - R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); - while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } - - /* Write soc rep. */ - R_TRY(WriteRegister(this->i2c_session, max17050::SocRep, vfsoc)); - - /* Clear power on reset. */ - R_TRY(ReadWriteValidateRegister(this->i2c_session, max17050::Status, 0x0002, 0x0000)); - - /* Set cgain. */ - R_TRY(WriteRegister(this->i2c_session, max17050::CGain, 0x7FFF)); - - return ResultSuccess(); - } - - Result Max17050Driver::SetMaximumShutdownTimerThreshold() { - return WriteRegister(this->i2c_session, max17050::ShdnTimer, 0xE000); - } - - bool Max17050Driver::IsPowerOnReset() { - /* Get the register. */ - u16 val; - R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::Status, std::addressof(val))); - - /* Extract the value. */ - return (val & 0x0002) != 0; - } - - Result Max17050Driver::LockVfSoc() { - return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0000); - } - - Result Max17050Driver::UnlockVfSoc() { - return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0080); - } - - Result Max17050Driver::LockModelTable() { - R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0000)); - R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x0000)); - return ResultSuccess(); - } - - Result Max17050Driver::UnlockModelTable() { - R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0059)); - R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x00C4)); - return ResultSuccess(); - } - - bool Max17050Driver::IsModelTableLocked() { - for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { - u16 val; - R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); - - if (val != 0) { - return false; - } - } - - return true; - } - - Result Max17050Driver::SetModelTable(const u16 *model_table) { - for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { - R_TRY(WriteRegister(this->i2c_session, max17050::ModelChrTblStart + i, model_table[i])); - } - - return ResultSuccess(); - } - - bool Max17050Driver::IsModelTableSet(const u16 *model_table) { - for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { - u16 val; - R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); - - if (val != model_table[i]) { - return false; - } - } - - return true; - } - - Result Max17050Driver::ReadInternalState() { - R_TRY(ReadRegister(this->i2c_session, max17050::RComp0, std::addressof(this->internal_state.rcomp0))); - R_TRY(ReadRegister(this->i2c_session, max17050::TempCo, std::addressof(this->internal_state.tempco))); - R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(this->internal_state.fullcap))); - R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(this->internal_state.cycles))); - R_TRY(ReadRegister(this->i2c_session, max17050::FullCapNom, std::addressof(this->internal_state.fullcapnom))); - R_TRY(ReadRegister(this->i2c_session, max17050::IAvgEmpty, std::addressof(this->internal_state.iavgempty))); - R_TRY(ReadRegister(this->i2c_session, max17050::QResidual00, std::addressof(this->internal_state.qresidual00))); - R_TRY(ReadRegister(this->i2c_session, max17050::QResidual10, std::addressof(this->internal_state.qresidual10))); - R_TRY(ReadRegister(this->i2c_session, max17050::QResidual20, std::addressof(this->internal_state.qresidual20))); - R_TRY(ReadRegister(this->i2c_session, max17050::QResidual30, std::addressof(this->internal_state.qresidual30))); - return ResultSuccess(); - } - - Result Max17050Driver::WriteInternalState() { - while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, this->internal_state.rcomp0)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, this->internal_state.tempco)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, this->internal_state.fullcapnom)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::IAvgEmpty, this->internal_state.iavgempty)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, this->internal_state.qresidual00)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, this->internal_state.qresidual10)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, this->internal_state.qresidual20)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, this->internal_state.qresidual30)) { /* ... */ } - - os::SleepThread(TimeSpan::FromMilliSeconds(350)); - - u16 fullcap0, socmix; - R_TRY(ReadRegister(this->i2c_session, max17050::FullCap0, std::addressof(fullcap0))); - R_TRY(ReadRegister(this->i2c_session, max17050::SocMix, std::addressof(socmix))); - - while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, static_cast((fullcap0 * socmix) / 0x6400))) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, this->internal_state.fullcap)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, 0x0C80)) { /* ... */ } - while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, this->internal_state.fullcapnom / 0x10)) { /* ... */ } - - os::SleepThread(TimeSpan::FromMilliSeconds(350)); - - while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, this->internal_state.cycles)) { /* ... */ } - if (this->internal_state.cycles >= 0x100) { - while (!WriteValidateRegister(this->i2c_session, max17050::LearnCfg, 0x2673)) { /* ... */ } - } - - return ResultSuccess(); - } - - Result Max17050Driver::GetSocRep(double *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::SocRep, std::addressof(val))); - - /* Set output. */ - *out = static_cast(val) * 0.00390625; - return ResultSuccess(); - } - - Result Max17050Driver::GetSocVf(double *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(val))); - - /* Set output. */ - *out = static_cast(val) * 0.00390625; - return ResultSuccess(); - } - - Result Max17050Driver::GetFullCapacity(double *out, double sense_resistor) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - AMS_ABORT_UNLESS(sense_resistor > 0.0); - - /* Read the values. */ - u16 cgain, fullcap; - R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); - R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(fullcap))); - - /* Set output. */ - *out = ((static_cast(fullcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); - return ResultSuccess(); - } - - Result Max17050Driver::GetRemainingCapacity(double *out, double sense_resistor) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - AMS_ABORT_UNLESS(sense_resistor > 0.0); - - /* Read the values. */ - u16 cgain, remcap; - R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); - R_TRY(ReadRegister(this->i2c_session, max17050::RemCapRep, std::addressof(remcap))); - - /* Set output. */ - *out = ((static_cast(remcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); - return ResultSuccess(); - } - - Result Max17050Driver::SetPercentageMinimumAlertThreshold(int percentage) { - return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0x00FF, static_cast(percentage)); - } - - Result Max17050Driver::SetPercentageMaximumAlertThreshold(int percentage) { - return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0xFF00, static_cast(static_cast(percentage)) << 8); - } - - Result Max17050Driver::SetPercentageFullThreshold(double percentage) { - #if defined(ATMOSPHERE_ARCH_ARM64) - const u16 val = vcvtd_n_s64_f64(percentage, BITSIZEOF(u8)); - #else - #error "Unknown architecture for floating point -> fixed point" - #endif - - return WriteRegister(this->i2c_session, max17050::FullSocThr, val); - } - - Result Max17050Driver::GetAverageCurrent(double *out, double sense_resistor) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - AMS_ABORT_UNLESS(sense_resistor > 0.0); - - /* Read the values. */ - u16 cgain, coff, avg_current; - R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); - R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); - R_TRY(ReadRegister(this->i2c_session, max17050::AverageCurrent, std::addressof(avg_current))); - - /* Set output. */ - *out = (((static_cast(avg_current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); - return ResultSuccess(); - } - - Result Max17050Driver::GetCurrent(double *out, double sense_resistor) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - AMS_ABORT_UNLESS(sense_resistor > 0.0); - - /* Read the values. */ - u16 cgain, coff, current; - R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); - R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); - R_TRY(ReadRegister(this->i2c_session, max17050::Current, std::addressof(current))); - - /* Set output. */ - *out = (((static_cast(current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); - return ResultSuccess(); - } - - Result Max17050Driver::GetNeedToRestoreParameters(bool *out) { - /* Get the register. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::MiscCfg, std::addressof(val))); - - /* Extract the value. */ - *out = (val & 0x8000) != 0; - return ResultSuccess(); - } - - Result Max17050Driver::SetNeedToRestoreParameters(bool en) { - return ReadWriteRegister(this->i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0); - } - - Result Max17050Driver::IsI2cShutdownEnabled(bool *out) { - /* Get the register. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::Config, std::addressof(val))); - - /* Extract the value. */ - *out = (val & 0x0040) != 0; - return ResultSuccess(); - } - - Result Max17050Driver::SetI2cShutdownEnabled(bool en) { - return ReadWriteRegister(this->i2c_session, max17050::Config, 0x0040, en ? 0x0040 : 0); - } - - Result Max17050Driver::GetStatus(u16 *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - return ReadRegister(this->i2c_session, max17050::Status, out); - } - - Result Max17050Driver::GetCycles(u16 *out) { - /* Get the register. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(val))); - - /* Extract the value. */ - *out = std::max(val, 0x60) - 0x60; - return ResultSuccess(); - } - - Result Max17050Driver::ResetCycles() { - return WriteRegister(this->i2c_session, max17050::Cycles, 0x0060); - } - - Result Max17050Driver::GetAge(double *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::Age, std::addressof(val))); - - /* Set output. */ - *out = static_cast(val) * 0.00390625; - return ResultSuccess(); - } - - Result Max17050Driver::GetTemperature(double *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::Temperature, std::addressof(val))); - - /* Set output. */ - *out = static_cast(val) * 0.00390625; - return ResultSuccess(); - } - - Result Max17050Driver::GetMaximumTemperature(u8 *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::MaxMinTemp, std::addressof(val))); - - /* Set output. */ - *out = static_cast(val >> 8); - return ResultSuccess(); - } - - Result Max17050Driver::SetTemperatureMinimumAlertThreshold(int c) { - return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0x00FF, static_cast(c)); - } - - Result Max17050Driver::SetTemperatureMaximumAlertThreshold(int c) { - return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0xFF00, static_cast(static_cast(c)) << 8); - } - - Result Max17050Driver::GetVCell(int *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::VCell, std::addressof(val))); - - /* Set output. */ - *out = (625 * (val >> 3)) / 1000; - return ResultSuccess(); - } - - Result Max17050Driver::GetAverageVCell(int *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::AverageVCell, std::addressof(val))); - - /* Set output. */ - *out = (625 * (val >> 3)) / 1000; - return ResultSuccess(); - } - - Result Max17050Driver::GetAverageVCellTime(double *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::FilterCfg, std::addressof(val))); - - /* Set output. */ - *out = 175.8 * ExponentiateTwoToPower(6 + ((val >> 4) & 7), 1.0); - return ResultSuccess(); - } - - Result Max17050Driver::GetOpenCircuitVoltage(int *out) { - /* Validate parameters. */ - AMS_ABORT_UNLESS(out != nullptr); - - /* Read the value. */ - u16 val; - R_TRY(ReadRegister(this->i2c_session, max17050::VFocV, std::addressof(val))); - - /* Set output. */ - *out = (1250 * (val >> 4)) / 1000; - return ResultSuccess(); - } - - Result Max17050Driver::SetVoltageMinimumAlertThreshold(int mv) { - return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0x00FF, static_cast(util::DivideUp(mv, 20))); - } - - Result Max17050Driver::SetVoltageMaximumAlertThreshold(int mv) { - return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0xFF00, static_cast(static_cast(mv / 20)) << 8); - } - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp deleted file mode 100644 index 7d01e30..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::powctl::impl::board::nintendo_nx { - - namespace max17050 { - - struct InternalState { - u16 rcomp0; - u16 tempco; - u16 fullcap; - u16 cycles; - u16 fullcapnom; - u16 iavgempty; - u16 qresidual00; - u16 qresidual10; - u16 qresidual20; - u16 qresidual30; - }; - - } - - class Max17050Driver { - private: - os::SdkMutex mutex; - int init_count; - i2c::I2cSession i2c_session; - max17050::InternalState internal_state; - private: - Result InitializeSession(const char *battery_vendor, u8 battery_version); - Result SetMaximumShutdownTimerThreshold(); - - bool IsPowerOnReset(); - Result LockVfSoc(); - Result UnlockVfSoc(); - Result LockModelTable(); - Result UnlockModelTable(); - bool IsModelTableLocked(); - Result SetModelTable(const u16 *model_table); - bool IsModelTableSet(const u16 *model_table); - public: - Max17050Driver() : mutex(), init_count(0), i2c_session(), internal_state() { - /* ... */ - } - - void Initialize(const char *battery_vendor, u8 battery_version) { - std::scoped_lock lk(this->mutex); - if ((this->init_count++) == 0) { - /* Initialize i2c library. */ - i2c::InitializeEmpty(); - - /* Open session. */ - R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050)); - - /* Initialize session. */ - R_ABORT_UNLESS(this->InitializeSession(battery_vendor, battery_version)); - - /* Set shutdown timer threshold to the maximum value. */ - R_ABORT_UNLESS(this->SetMaximumShutdownTimerThreshold()); - } - } - - void Finalize() { - std::scoped_lock lk(this->mutex); - if ((--this->init_count) == 0) { - /* Close session. */ - i2c::CloseSession(this->i2c_session); - - /* Finalize i2c library. */ - i2c::Finalize(); - } - } - - Result ReadInternalState(); - Result WriteInternalState(); - - void GetInternalState(max17050::InternalState *dst) { - *dst = this->internal_state; - } - - void SetInternalState(const max17050::InternalState &src) { - this->internal_state = src; - } - - Result GetSocRep(double *out); - Result GetSocVf(double *out); - - Result GetFullCapacity(double *out, double sense_resistor); - Result GetRemainingCapacity(double *out, double sense_resistor); - - Result SetPercentageMinimumAlertThreshold(int percentage); - Result SetPercentageMaximumAlertThreshold(int percentage); - - Result SetPercentageFullThreshold(double percentage); - - Result GetAverageCurrent(double *out, double sense_resistor); - Result GetCurrent(double *out, double sense_resistor); - - Result GetNeedToRestoreParameters(bool *out); - Result SetNeedToRestoreParameters(bool en); - - Result IsI2cShutdownEnabled(bool *out); - Result SetI2cShutdownEnabled(bool en); - - Result GetStatus(u16 *out); - - Result GetCycles(u16 *out); - Result ResetCycles(); - - Result GetAge(double *out); - - Result GetTemperature(double *out); - - Result GetMaximumTemperature(u8 *out); - - Result SetTemperatureMinimumAlertThreshold(int c); - Result SetTemperatureMaximumAlertThreshold(int c); - - Result GetVCell(int *out); - Result GetAverageVCell(int *out); - Result GetAverageVCellTime(double *out); - - Result GetOpenCircuitVoltage(int *out); - - Result SetVoltageMinimumAlertThreshold(int mv); - Result SetVoltageMaximumAlertThreshold(int mv); - }; - -} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp deleted file mode 100644 index d431099..0000000 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::powctl::impl { - - constexpr inline const TimeSpan PowerControlRetryTimeout = TimeSpan::FromSeconds(10); - constexpr inline const TimeSpan PowerControlRetryInterval = TimeSpan::FromMilliSeconds(20); - - #define AMS_POWCTL_R_TRY_WITH_RETRY(__EXPR__) \ - ({ \ - TimeSpan __powctl_retry_current_time = 0; \ - while (true) { \ - const Result __powctl_retry_result = (__EXPR__); \ - if (R_SUCCEEDED(__powctl_retry_result)) { \ - break; \ - } \ - \ - __powctl_retry_current_time += PowerControlRetryInterval; \ - R_UNLESS(__powctl_retry_current_time < PowerControlRetryTimeout, __powctl_retry_result); \ - \ - os::SleepThread(PowerControlRetryInterval); \ - } \ - }) - - -} diff --git a/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp b/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp index 6a9623f..7df69d2 100644 --- a/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp +++ b/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp @@ -19,7 +19,7 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include "board/nintendo_nx/powctl_board_impl.hpp" + #include "board/nintendo/nx/powctl_board_impl.hpp" namespace ams::powctl::impl::board { using namespace ams::powctl::impl::board::nintendo_nx; diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp new file mode 100644 index 0000000..1b1849d --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp @@ -0,0 +1,66 @@ +/* + * 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 "pwm_impl_pwm_driver_api.hpp" +#include "pwm_pwm_driver_impl.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline const dd::PhysicalAddress PwmRegistersPhysicalAddress = 0x7000A000; + constexpr inline size_t PwmRegistersSize = 0x100; + + constexpr const ChannelDefinition SupportedChannels[] = { + { pwm::DeviceCode_LcdBacklight, 0 }, + { pwm::DeviceCode_CpuFan, 1 }, + }; + + } + + Result InitializePwmDriver() { + /* Get the memory resource with which to allocate our driver/devices. */ + auto *memory_resource = ddsf::GetMemoryResource(); + + /* Allocate storage for our driver. */ + auto *driver_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); + AMS_ABORT_UNLESS(driver_storage != nullptr); + + /* Create our driver. */ + auto *driver = new (static_cast(driver_storage)) PwmDriverImpl(PwmRegistersPhysicalAddress, PwmRegistersSize, SupportedChannels, util::size(SupportedChannels)); + + /* Register our driver. */ + pwm::driver::RegisterDriver(driver); + + /* Create our devices. */ + for (const auto &entry : SupportedChannels) { + auto *device_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); + AMS_ABORT_UNLESS(device_storage != nullptr); + + /* Create our driver. */ + auto *device = new (static_cast(device_storage)) PwmDeviceImpl(entry.channel_id); + + /* Register the device with our driver. */ + driver->RegisterDevice(device); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(pwm::driver::RegisterDeviceCode(entry.device_code, device)); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.hpp new file mode 100644 index 0000000..9ce8137 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.hpp @@ -0,0 +1,28 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + struct ChannelDefinition { + DeviceCode device_code; + int channel_id; + }; + + Result InitializePwmDriver(); + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.cpp new file mode 100644 index 0000000..353148e --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.cpp @@ -0,0 +1,255 @@ +/* + * 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 "pwm_pwm_driver_impl.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline u32 PwmClockRateHz = 45'333'333; + + constexpr inline TimeSpan DefaultChannelPeriod = TimeSpan::FromMilliSeconds(10); + + constexpr inline int MaxDuty = 0x100; + + template + T DivideRoundUp(T a, T b) { + return (a + (b / 2)) / b; + } + + } + + PwmDriverImpl::PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nc) : registers_phys_addr(paddr), registers_size(sz), channels(c), num_channels(nc), registers(0) { + /* ... */ + } + + void PwmDriverImpl::PowerOn() { + /* Initialize pcv driver. */ + pcv::Initialize(); + + /* Setup clock/power for pwm. */ + R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, true)); + R_ABORT_UNLESS(pcv::SetClockEnabled(pcv::Module_Pwm, true)); + R_ABORT_UNLESS(pcv::SetClockRate(pcv::Module_Pwm, PwmClockRateHz)); + R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, false)); + } + + void PwmDriverImpl::PowerOff() { + /* Disable clock and hold pwm in reset. */ + /* NOTE: Nintendo does not check this succeeds. */ + pcv::SetClockEnabled(pcv::Module_Pwm, false); + pcv::SetReset(pcv::Module_Pwm, true); + + /* Finalize pcv driver. */ + pcv::Finalize(); + } + + void PwmDriverImpl::InitializeDriver() { + /* Get the registers virtual address. */ + this->registers = dd::QueryIoMapping(this->registers_phys_addr, this->registers_size); + AMS_ABORT_UNLESS(this->registers != 0); + + /* Setup power to pwm. */ + this->PowerOn(); + } + + void PwmDriverImpl::FinalizeDriver() { + /* Shut down power to pwm. */ + this->PowerOff(); + } + + Result PwmDriverImpl::InitializeDevice(IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Configure initial settings. */ + /* NOTE: None of these results are checked. */ + this->SetEnabled(device, false); + this->SetDuty(device, 0); + this->SetPeriod(device, DefaultChannelPeriod); + return ResultSuccess(); + } + + void PwmDriverImpl::FinalizeDevice(IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Nothing to do here. */ + AMS_UNUSED(device); + } + + Result PwmDriverImpl::SetPeriod(IPwmDevice *device, TimeSpan period) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Verify the period is valid. */ + const auto ns = period.GetNanoSeconds(); + R_UNLESS(ns > 0, pwm::ResultInvalidArgument()); + + /* Convert the ns to a desired frequency (rounding up). */ + const auto hz = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), ns); + R_UNLESS(hz > 0, pwm::ResultInvalidArgument()); + + /* Convert the frequency to a pfm value. */ + const u32 pfm = std::min(std::max(DivideRoundUp(PwmClockRateHz, hz * 256), 1) - 1, 0x1FFF); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the period. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PFM, pfm)); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetPeriod(TimeSpan *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the pfm value. */ + const u32 pfm = reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PFM)); + + /* Convert it to a frequency. */ + /* pfm = ((ClockRate / (hz * 256)) - 1) -> hz = (ClockRate / ((pfm + 1) * 256)) */ + const auto hz = DivideRoundUp(PwmClockRateHz, (pfm + 1) * 256); + + /* Convert the frequency to a period. */ + const auto ns = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), hz); + + /* Set the output. */ + *out = TimeSpan::FromNanoSeconds(ns); + return ResultSuccess(); + } + + Result PwmDriverImpl::SetDuty(IPwmDevice *device, int duty) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Validate the duty. */ + R_UNLESS(0 <= duty && duty <= MaxDuty, pwm::ResultInvalidArgument()); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the duty. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PWM, static_cast(duty))); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetDuty(int *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the duty. */ + *out = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); + return ResultSuccess(); + } + + Result PwmDriverImpl::SetScale(IPwmDevice *device, double scale) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Convert the scale to a duty. */ + const int duty = static_cast(((scale * 256.0) / 100.0) + 0.5); + + /* Set the duty. */ + return this->SetDuty(device, duty); + } + + Result PwmDriverImpl::GetScale(double *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the duty. */ + const int duty = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); + + /* Convert to scale. */ + *out = (static_cast(duty) * 100.0) / 256.0; + return ResultSuccess(); + } + + Result PwmDriverImpl::SetEnabled(IPwmDevice *device, bool en) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the enable. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM_SEL(PWM_CSR_ENB, en, ENABLE, DISABLE)); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetEnabled(bool *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the enable. */ + *out = reg::HasValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, ENABLE)); + return ResultSuccess(); + } + + Result PwmDriverImpl::Suspend() { + /* Suspend each device. */ + this->ForEachDevice([&](ddsf::IDevice &device) -> bool { + /* Convert the device to a pwm device. */ + auto &pwm_device = device.SafeCastTo(); + + /* Cache the suspend value. */ + pwm_device.SetSuspendValue(reg::Read(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR)); + + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(pwm_device); + + /* Disable the device. */ + reg::ReadWrite(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, DISABLE)); + + /* Continue to the next device. */ + return true; + }); + + /* Disable clock to pwm. */ + return pcv::SetClockEnabled(pcv::Module_Pwm, false); + } + + void PwmDriverImpl::Resume() { + /* Power on. */ + this->PowerOn(); + + /* Resume each device. */ + this->ForEachDevice([&](ddsf::IDevice &device) -> bool { + /* Convert the device to a pwm device. */ + auto &pwm_device = device.SafeCastTo(); + + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(pwm_device); + + /* Write the device's suspend value. */ + reg::Write(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, pwm_device.GetSuspendValue()); + + /* Continue to the next device. */ + return true; + }); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.hpp new file mode 100644 index 0000000..d04e841 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_pwm_driver_impl.hpp @@ -0,0 +1,85 @@ +/* + * 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 . + */ +#pragma once +#include +#include "pwm_impl_pwm_driver_api.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + class PwmDeviceImpl : public ::ams::pwm::driver::IPwmDevice { + NON_COPYABLE(PwmDeviceImpl); + NON_MOVEABLE(PwmDeviceImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDeviceImpl, ::ams::pwm::driver::IPwmDevice); + private: + os::SdkMutex suspend_mutex; + u32 suspend_value; + public: + PwmDeviceImpl(int channel) : IPwmDevice(channel), suspend_mutex(), suspend_value() { /* ... */ } + + void SetSuspendValue(u32 v) { this->suspend_value = v; } + u32 GetSuspendValue() const { return this->suspend_value; } + + void lock() { return this->suspend_mutex.lock(); } + void unlock() { return this->suspend_mutex.unlock(); } + }; + + class PwmDriverImpl : public ::ams::pwm::driver::IPwmDriver { + NON_COPYABLE(PwmDriverImpl); + NON_MOVEABLE(PwmDriverImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDriverImpl, ::ams::pwm::driver::IPwmDriver); + private: + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + const ChannelDefinition *channels; + size_t num_channels; + uintptr_t registers; + private: + ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice &device) { + return registers + PWM_CONTROLLER_PWM_CHANNEL_OFFSET(device.GetChannelIndex()); + } + + ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice *device) { + return this->GetRegistersFor(*device); + } + + void PowerOn(); + void PowerOff(); + public: + PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nsc); + + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializeDevice(IPwmDevice *device) override; + virtual void FinalizeDevice(IPwmDevice *device) override; + + virtual Result SetPeriod(IPwmDevice *device, TimeSpan period) override; + virtual Result GetPeriod(TimeSpan *out, IPwmDevice *device) override; + + virtual Result SetDuty(IPwmDevice *device, int duty) override; + virtual Result GetDuty(int *out, IPwmDevice *device) override; + + virtual Result SetScale(IPwmDevice *device, double scale) override; + virtual Result GetScale(double *out, IPwmDevice *device) override; + + virtual Result SetEnabled(IPwmDevice *device, bool en) override; + virtual Result GetEnabled(bool *out, IPwmDevice *device) override; + + virtual Result Suspend() override; + virtual void Resume() override; + }; + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/pwm_driver_api.cpp new file mode 100644 index 0000000..0b8442e --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/pwm_driver_api.cpp @@ -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 . + */ +#include +#include "impl/pwm_impl_pwm_driver_api.hpp" + +namespace ams::pwm::driver::board::nintendo_nx { + + void Initialize() { + R_ABORT_UNLESS(impl::InitializePwmDriver()); + /* TODO: R_ABORT_UNLESS(impl::InitializePmcDriver()); */ + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp deleted file mode 100644 index 1b1849d..0000000 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 "pwm_impl_pwm_driver_api.hpp" -#include "pwm_pwm_driver_impl.hpp" - -namespace ams::pwm::driver::board::nintendo_nx::impl { - - namespace { - - constexpr inline const dd::PhysicalAddress PwmRegistersPhysicalAddress = 0x7000A000; - constexpr inline size_t PwmRegistersSize = 0x100; - - constexpr const ChannelDefinition SupportedChannels[] = { - { pwm::DeviceCode_LcdBacklight, 0 }, - { pwm::DeviceCode_CpuFan, 1 }, - }; - - } - - Result InitializePwmDriver() { - /* Get the memory resource with which to allocate our driver/devices. */ - auto *memory_resource = ddsf::GetMemoryResource(); - - /* Allocate storage for our driver. */ - auto *driver_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); - AMS_ABORT_UNLESS(driver_storage != nullptr); - - /* Create our driver. */ - auto *driver = new (static_cast(driver_storage)) PwmDriverImpl(PwmRegistersPhysicalAddress, PwmRegistersSize, SupportedChannels, util::size(SupportedChannels)); - - /* Register our driver. */ - pwm::driver::RegisterDriver(driver); - - /* Create our devices. */ - for (const auto &entry : SupportedChannels) { - auto *device_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); - AMS_ABORT_UNLESS(device_storage != nullptr); - - /* Create our driver. */ - auto *device = new (static_cast(device_storage)) PwmDeviceImpl(entry.channel_id); - - /* Register the device with our driver. */ - driver->RegisterDevice(device); - - /* Register the device code with our driver. */ - R_ABORT_UNLESS(pwm::driver::RegisterDeviceCode(entry.device_code, device)); - } - - return ResultSuccess(); - } - -} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp deleted file mode 100644 index 9ce8137..0000000 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::pwm::driver::board::nintendo_nx::impl { - - struct ChannelDefinition { - DeviceCode device_code; - int channel_id; - }; - - Result InitializePwmDriver(); - -} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp deleted file mode 100644 index 353148e..0000000 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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 "pwm_pwm_driver_impl.hpp" - -namespace ams::pwm::driver::board::nintendo_nx::impl { - - namespace { - - constexpr inline u32 PwmClockRateHz = 45'333'333; - - constexpr inline TimeSpan DefaultChannelPeriod = TimeSpan::FromMilliSeconds(10); - - constexpr inline int MaxDuty = 0x100; - - template - T DivideRoundUp(T a, T b) { - return (a + (b / 2)) / b; - } - - } - - PwmDriverImpl::PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nc) : registers_phys_addr(paddr), registers_size(sz), channels(c), num_channels(nc), registers(0) { - /* ... */ - } - - void PwmDriverImpl::PowerOn() { - /* Initialize pcv driver. */ - pcv::Initialize(); - - /* Setup clock/power for pwm. */ - R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, true)); - R_ABORT_UNLESS(pcv::SetClockEnabled(pcv::Module_Pwm, true)); - R_ABORT_UNLESS(pcv::SetClockRate(pcv::Module_Pwm, PwmClockRateHz)); - R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, false)); - } - - void PwmDriverImpl::PowerOff() { - /* Disable clock and hold pwm in reset. */ - /* NOTE: Nintendo does not check this succeeds. */ - pcv::SetClockEnabled(pcv::Module_Pwm, false); - pcv::SetReset(pcv::Module_Pwm, true); - - /* Finalize pcv driver. */ - pcv::Finalize(); - } - - void PwmDriverImpl::InitializeDriver() { - /* Get the registers virtual address. */ - this->registers = dd::QueryIoMapping(this->registers_phys_addr, this->registers_size); - AMS_ABORT_UNLESS(this->registers != 0); - - /* Setup power to pwm. */ - this->PowerOn(); - } - - void PwmDriverImpl::FinalizeDriver() { - /* Shut down power to pwm. */ - this->PowerOff(); - } - - Result PwmDriverImpl::InitializeDevice(IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Configure initial settings. */ - /* NOTE: None of these results are checked. */ - this->SetEnabled(device, false); - this->SetDuty(device, 0); - this->SetPeriod(device, DefaultChannelPeriod); - return ResultSuccess(); - } - - void PwmDriverImpl::FinalizeDevice(IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Nothing to do here. */ - AMS_UNUSED(device); - } - - Result PwmDriverImpl::SetPeriod(IPwmDevice *device, TimeSpan period) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Verify the period is valid. */ - const auto ns = period.GetNanoSeconds(); - R_UNLESS(ns > 0, pwm::ResultInvalidArgument()); - - /* Convert the ns to a desired frequency (rounding up). */ - const auto hz = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), ns); - R_UNLESS(hz > 0, pwm::ResultInvalidArgument()); - - /* Convert the frequency to a pfm value. */ - const u32 pfm = std::min(std::max(DivideRoundUp(PwmClockRateHz, hz * 256), 1) - 1, 0x1FFF); - - /* Acquire exclusive access to the device registers. */ - std::scoped_lock lk(device->SafeCastTo()); - - /* Update the period. */ - reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PFM, pfm)); - - return ResultSuccess(); - } - - Result PwmDriverImpl::GetPeriod(TimeSpan *out, IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(device != nullptr); - - /* Get the pfm value. */ - const u32 pfm = reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PFM)); - - /* Convert it to a frequency. */ - /* pfm = ((ClockRate / (hz * 256)) - 1) -> hz = (ClockRate / ((pfm + 1) * 256)) */ - const auto hz = DivideRoundUp(PwmClockRateHz, (pfm + 1) * 256); - - /* Convert the frequency to a period. */ - const auto ns = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), hz); - - /* Set the output. */ - *out = TimeSpan::FromNanoSeconds(ns); - return ResultSuccess(); - } - - Result PwmDriverImpl::SetDuty(IPwmDevice *device, int duty) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Validate the duty. */ - R_UNLESS(0 <= duty && duty <= MaxDuty, pwm::ResultInvalidArgument()); - - /* Acquire exclusive access to the device registers. */ - std::scoped_lock lk(device->SafeCastTo()); - - /* Update the duty. */ - reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PWM, static_cast(duty))); - - return ResultSuccess(); - } - - Result PwmDriverImpl::GetDuty(int *out, IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(device != nullptr); - - /* Get the duty. */ - *out = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); - return ResultSuccess(); - } - - Result PwmDriverImpl::SetScale(IPwmDevice *device, double scale) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Convert the scale to a duty. */ - const int duty = static_cast(((scale * 256.0) / 100.0) + 0.5); - - /* Set the duty. */ - return this->SetDuty(device, duty); - } - - Result PwmDriverImpl::GetScale(double *out, IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(device != nullptr); - - /* Get the duty. */ - const int duty = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); - - /* Convert to scale. */ - *out = (static_cast(duty) * 100.0) / 256.0; - return ResultSuccess(); - } - - Result PwmDriverImpl::SetEnabled(IPwmDevice *device, bool en) { - /* Validate the device. */ - AMS_ASSERT(device != nullptr); - - /* Acquire exclusive access to the device registers. */ - std::scoped_lock lk(device->SafeCastTo()); - - /* Update the enable. */ - reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM_SEL(PWM_CSR_ENB, en, ENABLE, DISABLE)); - - return ResultSuccess(); - } - - Result PwmDriverImpl::GetEnabled(bool *out, IPwmDevice *device) { - /* Validate the device. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(device != nullptr); - - /* Get the enable. */ - *out = reg::HasValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, ENABLE)); - return ResultSuccess(); - } - - Result PwmDriverImpl::Suspend() { - /* Suspend each device. */ - this->ForEachDevice([&](ddsf::IDevice &device) -> bool { - /* Convert the device to a pwm device. */ - auto &pwm_device = device.SafeCastTo(); - - /* Cache the suspend value. */ - pwm_device.SetSuspendValue(reg::Read(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR)); - - /* Acquire exclusive access to the device. */ - std::scoped_lock lk(pwm_device); - - /* Disable the device. */ - reg::ReadWrite(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, DISABLE)); - - /* Continue to the next device. */ - return true; - }); - - /* Disable clock to pwm. */ - return pcv::SetClockEnabled(pcv::Module_Pwm, false); - } - - void PwmDriverImpl::Resume() { - /* Power on. */ - this->PowerOn(); - - /* Resume each device. */ - this->ForEachDevice([&](ddsf::IDevice &device) -> bool { - /* Convert the device to a pwm device. */ - auto &pwm_device = device.SafeCastTo(); - - /* Acquire exclusive access to the device. */ - std::scoped_lock lk(pwm_device); - - /* Write the device's suspend value. */ - reg::Write(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, pwm_device.GetSuspendValue()); - - /* Continue to the next device. */ - return true; - }); - } - -} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp deleted file mode 100644 index d04e841..0000000 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "pwm_impl_pwm_driver_api.hpp" - -namespace ams::pwm::driver::board::nintendo_nx::impl { - - class PwmDeviceImpl : public ::ams::pwm::driver::IPwmDevice { - NON_COPYABLE(PwmDeviceImpl); - NON_MOVEABLE(PwmDeviceImpl); - AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDeviceImpl, ::ams::pwm::driver::IPwmDevice); - private: - os::SdkMutex suspend_mutex; - u32 suspend_value; - public: - PwmDeviceImpl(int channel) : IPwmDevice(channel), suspend_mutex(), suspend_value() { /* ... */ } - - void SetSuspendValue(u32 v) { this->suspend_value = v; } - u32 GetSuspendValue() const { return this->suspend_value; } - - void lock() { return this->suspend_mutex.lock(); } - void unlock() { return this->suspend_mutex.unlock(); } - }; - - class PwmDriverImpl : public ::ams::pwm::driver::IPwmDriver { - NON_COPYABLE(PwmDriverImpl); - NON_MOVEABLE(PwmDriverImpl); - AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDriverImpl, ::ams::pwm::driver::IPwmDriver); - private: - dd::PhysicalAddress registers_phys_addr; - size_t registers_size; - const ChannelDefinition *channels; - size_t num_channels; - uintptr_t registers; - private: - ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice &device) { - return registers + PWM_CONTROLLER_PWM_CHANNEL_OFFSET(device.GetChannelIndex()); - } - - ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice *device) { - return this->GetRegistersFor(*device); - } - - void PowerOn(); - void PowerOff(); - public: - PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nsc); - - virtual void InitializeDriver() override; - virtual void FinalizeDriver() override; - - virtual Result InitializeDevice(IPwmDevice *device) override; - virtual void FinalizeDevice(IPwmDevice *device) override; - - virtual Result SetPeriod(IPwmDevice *device, TimeSpan period) override; - virtual Result GetPeriod(TimeSpan *out, IPwmDevice *device) override; - - virtual Result SetDuty(IPwmDevice *device, int duty) override; - virtual Result GetDuty(int *out, IPwmDevice *device) override; - - virtual Result SetScale(IPwmDevice *device, double scale) override; - virtual Result GetScale(double *out, IPwmDevice *device) override; - - virtual Result SetEnabled(IPwmDevice *device, bool en) override; - virtual Result GetEnabled(bool *out, IPwmDevice *device) override; - - virtual Result Suspend() override; - virtual void Resume() override; - }; - -} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp deleted file mode 100644 index 0b8442e..0000000 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 "impl/pwm_impl_pwm_driver_api.hpp" - -namespace ams::pwm::driver::board::nintendo_nx { - - void Initialize() { - R_ABORT_UNLESS(impl::InitializePwmDriver()); - /* TODO: R_ABORT_UNLESS(impl::InitializePmcDriver()); */ - } - -} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.cpp new file mode 100644 index 0000000..c9234e9 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.cpp @@ -0,0 +1,581 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_base_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + #define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(this->base_device->device_mutex) + + #else + + #define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() + + #endif + + void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const { + AMS_ABORT_UNLESS(out_c_size_mult != nullptr); + AMS_ABORT_UNLESS(out_read_bl_len != nullptr); + + /* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */ + *out_c_size_mult = static_cast((this->csd[2] >> 7) & 0x7); + *out_read_bl_len = static_cast((this->csd[4] >> 8) & 0xF); + } + + Result BaseDevice::SetLegacyMemoryCapacity() { + /* Get csize from the csd. */ + const u32 c_size = ((this->csd[3] >> 6) & 0x3FF) | ((this->csd[4] & 0x3) << 10); + + /* Get c_size_mult and read_bl_len. */ + u8 c_size_mult, read_bl_len; + this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len)); + + /* Validate the parameters. */ + R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue()); + + /* Set memory capacity. */ + this->memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9); + this->is_valid_memory_capacity = true; + + return ResultSuccess(); + } + + Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const { + /* Check if there are any errors at all. */ + R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0); + + /* Check errors individually. */ + #define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__()) + + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error); + + if (this->GetDeviceType() == DeviceType_Mmc) { + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError); + } + + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand); + AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange); + + #undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR + + return ResultSuccess(); + } + + DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const { + return static_cast((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift); + } + + Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const { + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(command_index, command_arg, CommandResponseType, is_busy); + R_TRY(this->host_controller->IssueCommand(std::addressof(command))); + + /* Get the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + this->host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType); + + /* Mask out the ignored status bits. */ + if (status_ignore_mask != 0) { + *out_response &= ~status_ignore_mask; + } + + /* Check the r1 response for errors. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + R_TRY(this->base_device->CheckDeviceStatus(*out_response)); + + /* Check the device state. */ + if (expected_state != DeviceState_Unknown) { + R_UNLESS(this->base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState()); + } + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::IssueCommandGoIdleState() const { + /* Issue the command. */ + Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false); + return this->host_controller->IssueCommand(std::addressof(command)); + } + + Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const { + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R2; + Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false); + R_TRY(this->host_controller->IssueCommand(std::addressof(command))); + + /* Copy the data out. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(u32))); + AMS_ABORT_UNLESS(dst_size >= DeviceCidSize); + this->host_controller->GetLastResponse(static_cast(dst), DeviceCidSize, CommandResponseType); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::IssueCommandSelectCard() const { + /* Get the command argument. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + const u32 arg = static_cast(this->base_device->GetRca()) << 16; + + /* Issue the command. */ + return this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown); + } + + Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const { + /* Get the command argument. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + const u32 arg = static_cast(this->base_device->GetRca()) << 16; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R2; + Command command(CommandIndex_SendCsd, arg, CommandResponseType, false); + R_TRY(this->host_controller->IssueCommand(std::addressof(command))); + + /* Copy the data out. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(u32))); + AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize); + this->host_controller->GetLastResponse(static_cast(dst), DeviceCsdSize, CommandResponseType); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const { + /* Get the command argument. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + const u32 arg = static_cast(this->base_device->GetRca()) << 16; + + /* Issue the command. */ + return this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask); + } + + Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const { + /* Issue the command. */ + return this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran); + } + + Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const { + /* Get the argument. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + const u32 arg = this->base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize; + + /* Get the command index and transfer direction. */ + const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock; + const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(command_index, arg, CommandResponseType, false); + TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true); + Result result = this->host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks); + + /* Handle the failure case. */ + if (R_FAILED(result)) { + /* Check if we were removed. */ + R_TRY(this->CheckRemoved()); + + /* By default, we'll want to return the result we just got. */ + Result result_to_return = result; + + /* Stop transmission. */ + u32 resp = 0; + result = this->host_controller->IssueStopTransmissionCommand(std::addressof(resp)); + if (R_SUCCEEDED(result)) { + result = this->base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand)); + if (R_FAILED(result)) { + result_to_return = result; + } + } + + /* Check if we were removed. */ + R_TRY(this->CheckRemoved()); + + /* Get the device status. */ + u32 device_status = 0; + result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand); + + /* If there's a device status error we don't already have, we prefer to return it. */ + if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) { + result_to_return = result; + } + + /* Return the result we chose. */ + return result_to_return; + } + + /* Get the responses. */ + u32 resp, st_resp; + this->host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + this->host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp)); + + /* Check the device status. */ + R_TRY(this->base_device->CheckDeviceStatus(resp)); + + /* Decide on what errors to ignore. */ + u32 status_ignore_mask = 0; + if (is_read) { + AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr); + if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) { + status_ignore_mask = DeviceStatus_AddressOutOfRange; + } + } + + /* Check the device status. */ + R_TRY(this->base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask)); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const { + /* Issue the read/write command. */ + AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr); + R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read)); + + /* Decide on what errors to ignore. */ + u32 status_ignore_mask = 0; + if (is_read) { + AMS_ABORT_UNLESS(this->base_device != nullptr); + if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) { + status_ignore_mask = DeviceStatus_AddressOutOfRange; + } + } + + /* Get and check the status. */ + u32 device_status; + R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask)); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) { + /* Verify that we can send the command. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + + /* If we want to read zero sectors, there's no work for us to do. */ + R_SUCCEED_IF(num_sectors == 0); + + /* Check that the buffer is big enough for the sectors we're reading. */ + AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors); + + /* Read sectors repeatedly until we've read all the ones we want. */ + u32 cur_sector_index = sector_index; + u32 remaining_sectors = num_sectors; + u8 *cur_buf = static_cast(buf); + while (remaining_sectors > 0) { + /* Determine how many sectors we can read in this iteration. */ + u32 cur_sectors = remaining_sectors; + if (sector_index_alignment > 0) { + AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0); + + const u32 max_sectors = this->host_controller->GetMaxTransferNumBlocks(); + if (remaining_sectors > max_sectors) { + cur_sectors = max_sectors - (max_sectors % sector_index_alignment); + } + } + + /* Try to perform the read/write. */ + u32 num_transferred_blocks = 0; + Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); + if (R_FAILED(result)) { + /* Check if we were removed. */ + R_TRY(this->CheckRemoved()); + + /* Log that we failed to read/write. */ + this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); + + /* Retry the read/write. */ + num_transferred_blocks = 0; + result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); + if (R_FAILED(result)) { + /* Check if we were removed. */ + R_TRY(this->CheckRemoved()); + + /* Log that we failed to read/write. */ + this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); + + /* Re-startup the connection, to see if that helps. */ + R_TRY(this->ReStartup()); + + /* Retry the read/write a third time. */ + num_transferred_blocks = 0; + result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); + if (R_FAILED(result)) { + /* Log that we failed after a re-startup. */ + this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); + return result; + } + + /* Log that we succeeded after a retry. */ + this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors); + + /* Increment the number of error corrections we've done. */ + ++this->num_read_write_error_corrections; + } + } + + /* Update our tracking variables. */ + AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks); + remaining_sectors -= num_transferred_blocks; + cur_sector_index += num_transferred_blocks; + cur_buf += num_transferred_blocks * SectorSize; + } + + return ResultSuccess(); + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Register the address. */ + return this->host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } + + void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Register the address. */ + return this->host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } + #endif + + Result BaseDeviceAccessor::Activate() { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is awake. */ + R_UNLESS(this->base_device->IsAwake(), sdmmc::ResultNotAwakened()); + + /* If the device is already active, we don't need to do anything. */ + R_SUCCEED_IF(this->base_device->IsActive()); + + /* Activate the base device. */ + auto activate_guard = SCOPE_GUARD { ++this->num_activation_failures; }; + R_TRY(this->OnActivate()); + + /* We successfully activated the device. */ + activate_guard.Cancel(); + this->base_device->SetActive(); + + return ResultSuccess(); + } + + void BaseDeviceAccessor::Deactivate() { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Deactivate the base device. */ + if (this->base_device->IsActive()) { + this->host_controller->Shutdown(); + this->base_device->Deactivate(); + } + } + + Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Perform the read/write. */ + auto rw_guard = SCOPE_GUARD { ++this->num_read_write_failures; }; + R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read)); + + /* We successfully performed the read/write. */ + rw_guard.Cancel(); + return ResultSuccess(); + } + + Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the current speed mode/bus width. */ + *out_speed_mode = this->host_controller->GetSpeedMode(); + *out_bus_width = this->host_controller->GetBusWidth(); + + /* Verify that we can get the status. */ + R_TRY(this->host_controller->GetInternalStatus()); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the capacity. */ + AMS_ABORT_UNLESS(out_sectors != nullptr); + *out_sectors = this->base_device->GetMemoryCapacity(); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the status. */ + R_TRY(this->IssueCommandSendStatus(out, 0)); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetOcr(u32 *out) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the ocr. */ + AMS_ABORT_UNLESS(out != nullptr); + *out = this->base_device->GetOcr(); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetRca(u16 *out) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the rca. */ + AMS_ABORT_UNLESS(out != nullptr); + *out = this->base_device->GetRca(); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetCid(void *out, size_t size) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the cid. */ + this->base_device->GetCid(out, size); + + return ResultSuccess(); + } + + Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Check that the device is accessible. */ + R_TRY(this->base_device->CheckAccessible()); + + /* Get the csd. */ + this->base_device->GetCsd(out, size); + + return ResultSuccess(); + } + + void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) { + /* Lock exclusive access of the base device. */ + AMS_ABORT_UNLESS(this->base_device != nullptr); + AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); + + /* Set the output error info. */ + AMS_ABORT_UNLESS(out_error_info != nullptr); + out_error_info->num_activation_failures = this->num_activation_failures; + out_error_info->num_activation_error_corrections = this->num_activation_error_corrections; + out_error_info->num_read_write_failures = this->num_read_write_failures; + out_error_info->num_read_write_error_corrections = this->num_read_write_error_corrections; + this->ClearErrorInfo(); + + /* Check if we should write logs. */ + if (out_log_size == nullptr) { + return; + } + + /* Check if we can write logs. */ + if (out_log_buffer == nullptr || log_buffer_size == 0) { + *out_log_size = 0; + return; + } + + /* Get and clear our logs. */ + #if defined(AMS_SDMMC_USE_LOGGER) + { + if (this->error_logger.HasLog()) { + this->PushErrorTimeStamp(); + + *out_log_size = this->error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size); + } else { + *out_log_size = 0; + } + } + #else + { + *out_log_size = 0; + } + #endif + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.hpp new file mode 100644 index 0000000..8cb0490 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.hpp @@ -0,0 +1,512 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_USE_LOGGER) + class Logger { + private: + static constexpr size_t LogLengthMax = 0x20; + static constexpr size_t LogCountMax = 0x10; + private: + char logs[LogCountMax][LogLengthMax]; + int log_index; + private: + void Clear() { + for (size_t i = 0; i < LogCountMax; ++i) { + this->logs[i][0] = '\0'; + } + this->log_index = 0; + } + + size_t Pop(char *dst, size_t dst_size) { + /* Decrease log index. */ + if ((--this->log_index) < 0) { + this->log_index = LogCountMax - 1; + } + + /* Check if we have a log. */ + if (this->logs[this->log_index][0] == '\0') { + return 0; + } + + /* Copy log to output. */ + const int len = ::ams::util::Strlcpy(dst, this->logs[this->log_index], dst_size); + + /* Clear the log we copied. */ + this->logs[this->log_index][0] = '\0'; + + return static_cast(len); + } + + public: + Logger() { + this->Clear(); + } + + void Push(const char *fmt, std::va_list vl) { + /* Format the log into the current buffer. */ + ::ams::util::TVSNPrintf(this->logs[this->log_index], LogLengthMax, fmt, vl); + + /* Update our log index. */ + if ((++this->log_index) >= static_cast(LogCountMax)) { + this->log_index = 0; + } + } + + void Push(const char *fmt, ...) { + std::va_list vl; + va_start(vl, fmt); + this->Push(fmt, vl); + va_end(vl); + } + + bool HasLog() const { + const int index = this->log_index > 0 ? this->log_index - 1 : static_cast(LogCountMax - 1); + return this->logs[index][0] != '\0'; + } + + size_t GetAndClearLogs(char *dst, size_t dst_size) { + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size > 0); + + /* Pop logs until we run out of them. */ + size_t total_len = 0; + while (true) { + /* Pop the current log. */ + const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len); + if (cur_len == 0) { + break; + } + + /* Check if the log exceeded the buffer size. */ + if (total_len + cur_len + 1 >= dst_size) { + break; + } + + /* Advance the total length. */ + total_len += cur_len; + + /* Check if there's space for our separator. */ + if (total_len + 3 >= dst_size) { + break; + } + + dst[total_len + 0] = ','; + dst[total_len + 1] = ' '; + total_len += 2; + } + + /* Ensure that the complete log fits in the buffer. */ + if (total_len >= dst_size) { + total_len = dst_size - 1; + } + + /* Ensure null termination. */ + dst[total_len] = '\0'; + + /* Clear any remaining logs. */ + this->Clear(); + + /* Return the length of the logs, including null terminator. */ + return total_len + 1; + } + }; + #endif + + enum DeviceType { + DeviceType_Mmc = 0, + DeviceType_SdCard = 1, + DeviceType_GcAsic = 2, + }; + + enum DeviceState { + DeviceState_Idle = 0, + DeviceState_Ready = 1, + DeviceState_Ident = 2, + DeviceState_Stby = 3, + DeviceState_Tran = 4, + DeviceState_Data = 5, + DeviceState_Rcv = 6, + DeviceState_Prg = 7, + DeviceState_Dis = 8, + DeviceState_Rsvd0 = 9, + DeviceState_Rsvd1 = 10, + DeviceState_Rsvd2 = 11, + DeviceState_Rsvd3 = 12, + DeviceState_Rsvd4 = 13, + DeviceState_Rsvd5 = 14, + DeviceState_RsvdIoMode = 15, + DeviceState_Unknown = 16, + }; + + enum DeviceStatus : u32 { + DeviceStatus_AkeSeqError = (1u << 3), + DeviceStatus_AppCmd = (1u << 5), + DeviceStatus_SwitchError = (1u << 7), + DeviceStatus_EraseReset = (1u << 13), + DeviceStatus_WpEraseSkip = (1u << 15), + DeviceStatus_CidCsdOverwrite = (1u << 16), + DeviceStatus_Error = (1u << 19), + DeviceStatus_CcError = (1u << 20), + DeviceStatus_DeviceEccFailed = (1u << 21), + DeviceStatus_IllegalCommand = (1u << 22), + DeviceStatus_ComCrcError = (1u << 23), + DeviceStatus_LockUnlockFailed = (1u << 24), + DeviceStatus_WpViolation = (1u << 26), + DeviceStatus_EraseParam = (1u << 27), + DeviceStatus_EraseSeqError = (1u << 28), + DeviceStatus_BlockLenError = (1u << 29), + DeviceStatus_AddressMisaligned = (1u << 30), + DeviceStatus_AddressOutOfRange = (1u << 31), + + DeviceStatus_CurrentStateShift = 9, + DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift), + + DeviceStatus_ErrorMask = (DeviceStatus_SwitchError | + DeviceStatus_EraseReset | + DeviceStatus_WpEraseSkip | + DeviceStatus_CidCsdOverwrite | + DeviceStatus_Error | + DeviceStatus_CcError | + DeviceStatus_DeviceEccFailed | + DeviceStatus_IllegalCommand | + DeviceStatus_ComCrcError | + DeviceStatus_LockUnlockFailed | + DeviceStatus_WpViolation | + DeviceStatus_EraseParam | + DeviceStatus_EraseSeqError | + DeviceStatus_BlockLenError | + DeviceStatus_AddressMisaligned | + DeviceStatus_AddressOutOfRange), + }; + + class BaseDevice { + private: + u32 ocr; + u8 cid[DeviceCidSize]; + u16 csd[DeviceCsdSize / sizeof(u16)]; + u32 memory_capacity; + bool is_high_capacity; + bool is_valid_ocr; + bool is_valid_cid; + bool is_valid_csd; + bool is_valid_high_capacity; + bool is_valid_memory_capacity; + bool is_active; + bool is_awake; + public: + #if defined(AMS_SDMMC_THREAD_SAFE) + mutable os::Mutex device_mutex; + public: + BaseDevice() : device_mutex(true) + #else + BaseDevice() + #endif + { + this->is_awake = true; + this->ocr = 0; + this->memory_capacity = 0; + this->is_high_capacity = false; + this->OnDeactivate(); + } + + void OnDeactivate() { + this->is_active = false; + this->is_valid_ocr = false; + this->is_valid_cid = false; + this->is_valid_csd = false; + this->is_valid_high_capacity = false; + this->is_valid_memory_capacity = false; + } + + bool IsAwake() const { + return this->is_awake; + } + + void Awaken() { + this->is_awake = true; + } + + void PutToSleep() { + this->is_awake = false; + } + + bool IsActive() const { + return this->is_active; + } + + void SetActive() { + this->is_active = true; + } + + virtual void Deactivate() { + this->OnDeactivate(); + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual os::EventType *GetRemovedEvent() const = 0; + #endif + + virtual DeviceType GetDeviceType() const = 0; + virtual u16 GetRca() const = 0; + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + void InitializeRemovedEvent() { + if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { + os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear); + } + } + + void FinalizeRemovedEvent() { + if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { + os::FinalizeEvent(removed_event); + } + } + + void SignalRemovedEvent() { + if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { + os::SignalEvent(removed_event); + } + } + + void ClearRemovedEvent() { + if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { + os::ClearEvent(removed_event); + } + } + + bool IsRemoved() const { + if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { + return os::TryWaitEvent(removed_event); + } + return false; + } + #endif + + Result CheckRemoved() const { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved()); + #endif + + return ResultSuccess(); + } + + Result CheckAccessible() const { + /* Check awake. */ + R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened()); + + /* Check active. */ + R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated()); + + /* Check removed. */ + R_TRY(this->CheckRemoved()); + + return ResultSuccess(); + } + + void SetHighCapacity(bool en) { + this->is_high_capacity = en; + this->is_valid_high_capacity = true; + } + + bool IsHighCapacity() const { + AMS_ABORT_UNLESS(this->is_valid_high_capacity); + return this->is_high_capacity; + } + + void SetOcr(u32 o) { + this->ocr = o; + this->is_valid_ocr = true; + } + + u32 GetOcr() const { + AMS_ABORT_UNLESS(this->is_valid_ocr); + return this->ocr; + } + + void SetCid(const void *src, size_t src_size) { + AMS_ABORT_UNLESS(src != nullptr); + AMS_ABORT_UNLESS(src_size >= DeviceCidSize); + std::memcpy(this->cid, src, DeviceCidSize); + this->is_valid_cid = true; + } + + void GetCid(void *dst, size_t dst_size) const { + AMS_ABORT_UNLESS(this->is_valid_cid); + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= DeviceCidSize); + std::memcpy(dst, this->cid, DeviceCidSize); + } + + void SetCsd(const void *src, size_t src_size) { + AMS_ABORT_UNLESS(src != nullptr); + AMS_ABORT_UNLESS(src_size >= DeviceCsdSize); + std::memcpy(this->csd, src, DeviceCsdSize); + this->is_valid_csd = true; + } + + void GetCsd(void *dst, size_t dst_size) const { + AMS_ABORT_UNLESS(this->is_valid_csd); + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize); + std::memcpy(dst, this->csd, DeviceCsdSize); + } + + void SetMemoryCapacity(u32 num_sectors) { + this->memory_capacity = num_sectors; + this->is_valid_memory_capacity = true; + } + + u32 GetMemoryCapacity() const { + AMS_ABORT_UNLESS(this->is_valid_memory_capacity); + return this->memory_capacity; + } + + void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const; + Result SetLegacyMemoryCapacity(); + + Result CheckDeviceStatus(u32 r1_resp) const; + DeviceState GetDeviceState(u32 r1_resp) const; + }; + + class BaseDeviceAccessor : public IDeviceAccessor { + private: + IHostController *host_controller; + BaseDevice *base_device; + u32 num_activation_failures; + u32 num_activation_error_corrections; + u32 num_read_write_failures; + u32 num_read_write_error_corrections; + #if defined(AMS_SDMMC_USE_LOGGER) + Logger error_logger; + #endif + private: + void ClearErrorInfo() { + this->num_activation_failures = 0; + this->num_activation_error_corrections = 0; + this->num_read_write_failures = 0; + this->num_read_write_error_corrections = 0; + } + protected: + explicit BaseDeviceAccessor(IHostController *hc) : host_controller(hc), base_device(nullptr) { + this->ClearErrorInfo(); + } + + IHostController *GetHostController() const { + return this->host_controller; + } + + void SetDevice(BaseDevice *bd) { + this->base_device = bd; + } + + Result CheckRemoved() const { + return this->base_device->CheckRemoved(); + } + + Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const; + + Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const { + u32 dummy; + return this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask); + } + + Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const { + return this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0); + } + + Result IssueCommandGoIdleState() const; + Result IssueCommandAllSendCid(void *dst, size_t dst_size) const; + Result IssueCommandSelectCard() const; + Result IssueCommandSendCsd(void *dst, size_t dst_size) const; + Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const; + + Result IssueCommandSendStatus(u32 status_ignore_mask) const { + u32 dummy; + return this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask); + } + + Result IssueCommandSendStatus() const { + return this->IssueCommandSendStatus(0); + } + + Result IssueCommandSetBlockLenToSectorSize() const; + Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const; + Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const; + Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read); + + void IncrementNumActivationErrorCorrections() { + ++this->num_activation_error_corrections; + } + + void PushErrorTimeStamp() { + #if defined(AMS_SDMMC_USE_LOGGER) + { + this->error_logger.Push("%u", static_cast(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds())); + } + #endif + } + + void PushErrorLog(bool with_timestamp, const char *fmt, ...) { + #if defined(AMS_SDMMC_USE_LOGGER) + { + std::va_list vl; + va_start(vl, fmt); + this->error_logger.Push(fmt, vl); + va_end(vl); + + if (with_timestamp) { + this->PushErrorTimeStamp(); + } + } + #else + { + AMS_UNUSED(with_timestamp, fmt); + } + #endif + } + + virtual Result OnActivate() = 0; + virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) = 0; + virtual Result ReStartup() = 0; + public: + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; + virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; + #endif + + virtual Result Activate() override; + virtual void Deactivate() override; + + virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) override; + virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) override; + + virtual Result GetMemoryCapacity(u32 *out_sectors) const override; + virtual Result GetDeviceStatus(u32 *out) const override; + virtual Result GetOcr(u32 *out) const override; + virtual Result GetRca(u16 *out) const override; + virtual Result GetCid(void *out, size_t size) const override; + virtual Result GetCsd(void *out, size_t size) const override; + + virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp new file mode 100644 index 0000000..1790380 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp @@ -0,0 +1,151 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_clock_reset_controller.hpp" +#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp" +#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp" + +namespace ams::sdmmc::impl::ClockResetController { + + namespace { + + constinit bool g_is_module_initialized[Module_Count] = {}; + + #if defined(AMS_SDMMC_THREAD_SAFE) + + constinit os::SdkMutex g_module_mutex; + + #define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex) + + #else + + #define AMS_SDMMC_LOCK_MODULE_MUTEX() + + #endif + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + constinit bool g_is_pcv_control = false; + + #define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control) + + #else + + #define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false) + + #endif + + } + + void Initialize(Module module) { + /* Acquire exclusive access to module state. */ + AMS_SDMMC_LOCK_MODULE_MUTEX(); + + /* Mark the module as initialized. */ + g_is_module_initialized[module] = true; + + /* Initialize the module. */ + AMS_SDMMC_IF_IS_PCV_CONTROL() { + ClockResetController::pcv::Initialize(module); + } else { + ClockResetController::reg::Initialize(module); + } + } + + void Finalize(Module module) { + /* Acquire exclusive access to module state. */ + AMS_SDMMC_LOCK_MODULE_MUTEX(); + + /* Finalize the module. */ + AMS_SDMMC_IF_IS_PCV_CONTROL() { + ClockResetController::pcv::Finalize(module); + } else { + ClockResetController::reg::Finalize(module); + } + + /* Mark the module as finalized. */ + g_is_module_initialized[module] = false; + } + + bool IsAvailable(Module module) { + AMS_SDMMC_IF_IS_PCV_CONTROL() { + return ClockResetController::pcv::IsAvailable(module); + } else { + return ClockResetController::reg::IsAvailable(module); + } + } + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void SwitchToPcvControl() { + /* Acquire exclusive access to module state. */ + AMS_SDMMC_LOCK_MODULE_MUTEX(); + + /* If we're already using pcv control, we don't need to do anything. */ + AMS_SDMMC_IF_IS_PCV_CONTROL() { + return; + } + + /* Finalize all modules. */ + for (int i = 0; i < Module_Count; ++i) { + if (g_is_module_initialized[i]) { + ClockResetController::reg::Finalize(static_cast(i)); + } + } + + /* Mark that we've switched to pcv control. */ + + /* Initialize modules using pcv control. */ + for (int i = 0; i < Module_Count; ++i) { + if (g_is_module_initialized[i]) { + ClockResetController::pcv::Initialize(static_cast(i)); + } + } + } + #endif + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { + AMS_SDMMC_IF_IS_PCV_CONTROL() { + return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency); + } else { + return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency); + } + } + + void AssertReset(Module module) { + AMS_SDMMC_IF_IS_PCV_CONTROL() { + return ClockResetController::pcv::AssertReset(module); + } else { + return ClockResetController::reg::AssertReset(module); + } + } + + void ReleaseReset(Module module, u32 target_frequency_khz) { + AMS_SDMMC_IF_IS_PCV_CONTROL() { + return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz); + } else { + return ClockResetController::reg::ReleaseReset(module, target_frequency_khz); + } + } + + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.hpp new file mode 100644 index 0000000..29ac276 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.hpp @@ -0,0 +1,44 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl::ClockResetController { + + enum Module { + #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + Module_Sdmmc1 = 0, + Module_Sdmmc2 = 1, + Module_Sdmmc3 = 2, + Module_Sdmmc4 = 3, + #endif + + Module_Count, + }; + + void Initialize(Module module); + void Finalize(Module module); + bool IsAvailable(Module module); + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void SwitchToPcvControl(); + #endif + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); + void AssertReset(Module module); + void ReleaseReset(Module module, u32 target_frequency_khz); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp new file mode 100644 index 0000000..8d8c39d --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp @@ -0,0 +1,95 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp" + +namespace ams::sdmmc::impl::ClockResetController::pcv { + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + + void Initialize(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not implemented"); + } + + void Finalize(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not implemented"); + } + + bool IsAvailable(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not implemented"); + } + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { + AMS_UNUSED(out_actual_frequency, module, target_frequency); + AMS_ABORT("PCV Control not implemented"); + } + + void AssertReset(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not implemented"); + } + + void ReleaseReset(Module module, u32 target_frequency_khz) { + AMS_UNUSED(module, target_frequency_khz); + AMS_ABORT("PCV Control not implemented"); + } + + #else + + void Initialize(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not supported"); + } + + void Finalize(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not supported"); + } + + bool IsAvailable(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not supported"); + } + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { + AMS_UNUSED(out_actual_frequency, module, target_frequency); + AMS_ABORT("PCV Control not supported"); + } + + void AssertReset(Module module) { + AMS_UNUSED(module); + AMS_ABORT("PCV Control not supported"); + } + + void ReleaseReset(Module module, u32 target_frequency_khz) { + AMS_UNUSED(module, target_frequency_khz); + AMS_ABORT("PCV Control not supported"); + } + + #endif + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp new file mode 100644 index 0000000..85144bf --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp @@ -0,0 +1,30 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_clock_reset_controller.hpp" + +namespace ams::sdmmc::impl::ClockResetController::pcv { + + void Initialize(Module module); + void Finalize(Module module); + bool IsAvailable(Module module); + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); + void AssertReset(Module module); + void ReleaseReset(Module module, u32 target_frequency_khz); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp new file mode 100644 index 0000000..3a49428 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp @@ -0,0 +1,391 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl::ClockResetController::reg { + + namespace { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + constinit os::SdkMutex g_init_mutex; + + #define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex) + + #else + + #define AMS_SDMMC_LOCK_INIT_MUTEX() + + #endif + + constinit bool g_is_initialized = false; + + struct ModuleInfo { + u32 target_frequency_khz; + u32 actual_frequency_khz; + }; + + constinit ModuleInfo g_module_infos[Module_Count] = {}; + + constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000); + constexpr inline size_t ClockResetControllerRegistersSize = 4_KB; + + constinit uintptr_t g_clkrst_registers_address = 0; + + [[maybe_unused]] void InitializePllc4() { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */ + constexpr u32 ExpectedPllc4Base = 0x58006804; + if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) { + return; + } + + /* Disable PLLC4_ENABLE, if it's currently set. */ + if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) { + ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE)); + } + + /* Operate on the register with PLLC4_ENABLE cleared. */ + { + /* Clear IDDQ, read to be sure it takes, wait 5 us. */ + ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF)); + ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE); + WaitMicroSeconds(5); + + /* Write the expected value sans IDDQ/PLLC4_ENABLE. */ + constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE), + CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ)); + ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask); + } + + /* Write PLLC4_ENABLE, and read to be sure our configuration takes. */ + ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE)); + ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE); + + /* Wait up to 1s for changes to take. */ + { + ManualTimer timer(1000); + while (true) { + /* Check if we're done. */ + if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) { + break; + } + + /* Check that we haven't timed out. */ + AMS_ABORT_UNLESS(timer.Update()); + } + } + } + + void InitializeLegacyTmClk() { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Configure the legacy tm clock as 12MHz. */ + ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66)); + + /* Enable clock to the legacy tm. */ + ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE)); + } + + bool IsResetReleased(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Get the reset bit from RST_DEVICES_* */ + switch (module) { + case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE)); + case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE)); + case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE)); + case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SetReset(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Set reset in RST_DEV_*_SET */ + switch (module) { + case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE)); + case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE)); + case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE)); + case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void ClearReset(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Set reset in RST_DEV_*_CLR */ + switch (module) { + case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE)); + case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE)); + case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE)); + case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + bool IsClockEnabled(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Get the enable bit from CLK_OUT_ENB_* */ + switch (module) { + case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE)); + case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE)); + case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE)); + case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SetClockEnable(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Set clock enable bit in CLK_ENB_*_SET */ + switch (module) { + case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE)); + case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE)); + case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE)); + case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void ClearClockEnable(Module module) { + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Set clock enable bit in CLK_ENB_*_CLR */ + switch (module) { + case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE)); + case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE)); + case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE)); + case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) { + /* Check that we can write to output. */ + AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr); + + /* Determine frequency/divisor. */ + u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0)); + u8 n; + switch (target_frequency_khz) { + case 25'000: + *out_actual_frequency_khz = 24'728; + n = 31; + break; + case 26'000: + *out_actual_frequency_khz = 25'500; + n = 30; + break; + case 40'800: + *out_actual_frequency_khz = 40'800; + n = 18; + break; + case 50'000: + *out_actual_frequency_khz = 48'000; + n = 15; + break; + case 52'000: + *out_actual_frequency_khz = 51'000; + n = 14; + break; + case 100'000: + #if defined(AMS_SDMMC_SET_PLLC4_BASE) + *out_actual_frequency_khz = 99'840; + n = 2; + clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2)); + #else + *out_actual_frequency_khz = 90'667; + n = 7; + #endif + break; + case 200'000: + #if defined(AMS_SDMMC_SET_PLLC4_BASE) + *out_actual_frequency_khz = 199'680; + n = 0; + if (module == Module_Sdmmc2 || module == Module_Sdmmc4) { + clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ)); + } else { + clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2)); + } + #else + *out_actual_frequency_khz = 163'200; + n = 3; + #endif + break; + case 208'000: + *out_actual_frequency_khz = 204'000; + n = 2; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Set frequencies in module info. */ + g_module_infos[module].target_frequency_khz = target_frequency_khz; + g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz; + + /* Check that we have registers we can write to. */ + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Update the clock source. */ + switch (module) { + case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast(n)); break; + case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast(n)); break; + case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast(n)); break; + case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast(n)); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void EnsureControl(Module module) { + /* Read from RST_DEVICES_* to be sure previous configuration takes. */ + switch (module) { + case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; + case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; + case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break; + case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + void Initialize(Module module) { + /* Initialization isn't module specific. */ + AMS_UNUSED(module); + + /* Acquire exclusive access to the initialization lock. */ + AMS_SDMMC_LOCK_INIT_MUTEX(); + + /* If we've already initialized, we don't need to do anything. */ + if (g_is_initialized) { + return; + } + + /* Clear module infos. */ + std::memset(g_module_infos, 0, sizeof(g_module_infos)); + + /* Get the registers address. */ + g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize); + AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); + + /* Perform register initialization. */ + #if defined(AMS_SDMMC_SET_PLLC4_BASE) + InitializePllc4(); + #endif + InitializeLegacyTmClk(); + + /* Mark that we've initialized. */ + g_is_initialized = true; + } + + void Finalize(Module module) { + /* Nothing is needed for finalization. */ + AMS_UNUSED(module); + } + + bool IsAvailable(Module module) { + return IsResetReleased(module) && IsClockEnabled(module); + } + + void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) { + /* If we're not changing the clock frequency, we don't need to do anything. */ + if (target_frequency_khz == g_module_infos[module].target_frequency_khz) { + *out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz; + return; + } + + /* Temporarily disable clock. */ + const bool clock_enabled = IsClockEnabled(module); + if (clock_enabled) { + ClearClockEnable(module); + } + + /* Set the clock source. */ + SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz); + + /* Re-enable clock, if we should. */ + if (clock_enabled) { + SetClockEnable(module); + } + + /* Ensure that our configuration takes. */ + EnsureControl(module); + } + + void AssertReset(Module module) { + /* Set reset and disable clock. */ + SetReset(module); + ClearClockEnable(module); + + /* Ensure that our configuration takes. */ + EnsureControl(module); + } + + void ReleaseReset(Module module, u32 target_frequency_khz) { + /* Disable clock if it's enabled. */ + if (IsClockEnabled(module)) { + ClearClockEnable(module); + } + + /* Set reset. */ + SetReset(module); + + /* Set the clock source. */ + u32 actual_source_frequency_khz; + SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz); + + /* Enable clock. */ + SetClockEnable(module); + + /* Ensure that our configuration takes. */ + EnsureControl(module); + + /* Wait 100 clocks. */ + WaitClocks(100, actual_source_frequency_khz); + + /* Clear reset. */ + ClearReset(module); + + /* Ensure that our configuration takes. */ + EnsureControl(module); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp new file mode 100644 index 0000000..4043037 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp @@ -0,0 +1,30 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_clock_reset_controller.hpp" + +namespace ams::sdmmc::impl::ClockResetController::reg { + + void Initialize(Module module); + void Finalize(Module module); + bool IsAvailable(Module module); + + void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); + void AssertReset(Module module); + void ReleaseReset(Module module, u32 target_frequency_khz); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.cpp new file mode 100644 index 0000000..3529b85 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.cpp @@ -0,0 +1,270 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) +#include "sdmmc_device_detector.hpp" + +namespace ams::sdmmc::impl { + + bool DeviceDetector::IsCurrentInserted() { + return gpio::GetValue(std::addressof(this->gpio_pad_session)) == this->inserted_gpio_value; + } + + void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) { + if (!prev_inserted && !cur_inserted) { + /* Not inserted -> Not inserted, nothing to do. */ + } else if (!prev_inserted && cur_inserted) { + /* Card was inserted. */ + if (this->callback_info.inserted_callback != nullptr) { + this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg); + } + } else if (prev_inserted && !cur_inserted) { + /* Card was removed. */ + if (this->callback_info.removed_callback != nullptr) { + this->callback_info.removed_callback(this->callback_info.removed_callback_arg); + } + } else /* if (prev_inserted && cur_inserted) */ { + /* Card was removed, and then inserted. */ + if (this->callback_info.removed_callback != nullptr) { + this->callback_info.removed_callback(this->callback_info.removed_callback_arg); + } + + if (this->callback_info.inserted_callback != nullptr) { + this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg); + } + } + } + + void DeviceDetector::DetectorThread() { + /* Initialize the gpio session. */ + sm::DoWithSession([] { gpio::Initialize(); }); + gpio::OpenSession(std::addressof(this->gpio_pad_session), this->gpio_device_code); + gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Input); + gpio::SetDebounceTime(std::addressof(this->gpio_pad_session), this->gpio_debounce_ms); + gpio::SetDebounceEnabled(std::addressof(this->gpio_pad_session), true); + gpio::SetInterruptMode(std::addressof(this->gpio_pad_session), gpio::InterruptMode_AnyEdge); + + /* Get the gpio session's interrupt event. */ + os::SystemEventType gpio_event; + R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(this->gpio_pad_session))); + + /* Initialize and link waitable holders. */ + os::WaitableManagerType wait_manager; + os::WaitableHolderType detector_thread_end_holder; + os::WaitableHolderType request_sleep_wake_event_holder; + os::WaitableHolderType gpio_event_holder; + os::InitializeWaitableManager(std::addressof(wait_manager)); + os::InitializeWaitableHolder(std::addressof(detector_thread_end_holder), std::addressof(this->detector_thread_end_event)); + os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(detector_thread_end_holder)); + os::InitializeWaitableHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(this->request_sleep_wake_event)); + os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(request_sleep_wake_event_holder)); + os::InitializeWaitableHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event)); + os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder)); + + /* Wait before detecting the initial state of the card. */ + os::SleepThread(TimeSpan::FromMilliSeconds(this->gpio_debounce_ms)); + bool cur_inserted = this->IsCurrentInserted(); + this->is_prev_inserted = cur_inserted; + + /* Set state as awake. */ + this->state = State_Awake; + os::SignalEvent(std::addressof(this->ready_device_status_event)); + + /* Enable interrupts to be informed of device status. */ + gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true); + + /* Wait, servicing our events. */ + while (true) { + /* Get the signaled holder. */ + os::WaitableHolderType *signaled_holder = os::WaitAny(std::addressof(wait_manager)); + + /* Process the holder. */ + bool insert_change = false; + if (signaled_holder == std::addressof(detector_thread_end_holder)) { + /* We should kill ourselves. */ + os::ClearEvent(std::addressof(this->detector_thread_end_event)); + this->state = State_Finalized; + break; + } else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) { + /* A request for us to sleep/wake has come in, so we'll acknowledge it. */ + os::ClearEvent(std::addressof(this->request_sleep_wake_event)); + this->state = State_Sleep; + os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event)); + + /* Temporarily unlink our interrupt event. */ + os::UnlinkWaitableHolder(std::addressof(gpio_event_holder)); + + /* Wait to be signaled. */ + signaled_holder = os::WaitAny(std::addressof(wait_manager)); + + /* Link our interrupt event back in. */ + os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder)); + + /* We're awake again. Either because we should exit, or because we were asked to wake up. */ + os::ClearEvent(std::addressof(this->request_sleep_wake_event)); + this->state = State_Awake; + os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event)); + + /* If we were asked to exit, do so. */ + if (signaled_holder == std::addressof(detector_thread_end_holder)) { + /* We should kill ourselves. */ + os::ClearEvent(std::addressof(this->detector_thread_end_event)); + this->state = State_Finalized; + break; + } else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ { + if ((this->force_detection) || + (({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), this->gpio_device_code)) && active; })) || + (os::TryWaitSystemEvent(std::addressof(gpio_event))) || + (this->is_prev_inserted != this->IsCurrentInserted())) + { + insert_change = true; + } + } + } else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ { + /* An event was detected. */ + insert_change = true; + } + + /* Handle an insert change, if one occurred. */ + if (insert_change) { + /* Call the relevant callback, if we have one. */ + if (this->device_detection_event_callback != nullptr) { + this->device_detection_event_callback(this->device_detection_event_callback_arg); + } + + /* Clear the interrupt event. */ + os::ClearSystemEvent(std::addressof(gpio_event)); + gpio::ClearInterruptStatus(std::addressof(this->gpio_pad_session)); + gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true); + + /* Update insertion status. */ + cur_inserted = this->IsCurrentInserted(); + this->HandleDeviceStatus(this->is_prev_inserted, cur_inserted); + this->is_prev_inserted = cur_inserted; + } + } + + /* Disable interrupts to our gpio event. */ + gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), false); + + /* Finalize and unlink waitable holders. */ + os::UnlinkWaitableHolder(std::addressof(gpio_event_holder)); + os::FinalizeWaitableHolder(std::addressof(gpio_event_holder)); + os::UnlinkWaitableHolder(std::addressof(request_sleep_wake_event_holder)); + os::FinalizeWaitableHolder(std::addressof(request_sleep_wake_event_holder)); + os::UnlinkWaitableHolder(std::addressof(detector_thread_end_holder)); + os::FinalizeWaitableHolder(std::addressof(detector_thread_end_holder)); + os::FinalizeWaitableManager(std::addressof(wait_manager)); + + /* Finalize the gpio session. */ + gpio::UnbindInterrupt(std::addressof(this->gpio_pad_session)); + gpio::CloseSession(std::addressof(this->gpio_pad_session)); + gpio::Finalize(); + } + + void DeviceDetector::Initialize(CallbackInfo *ci) { + /* Transition our state from finalized to initializing. */ + AMS_ABORT_UNLESS(this->state == State_Finalized); + this->state = State_Initializing; + + /* Set our callback infos. */ + this->callback_info = *ci; + + /* Initialize our events. */ + os::InitializeEvent(std::addressof(this->ready_device_status_event), false, os::EventClearMode_ManualClear); + os::InitializeEvent(std::addressof(this->request_sleep_wake_event), false, os::EventClearMode_ManualClear); + os::InitializeEvent(std::addressof(this->acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear); + os::InitializeEvent(std::addressof(this->detector_thread_end_event), false, os::EventClearMode_ManualClear); + + /* Create and start the detector thread. */ + os::CreateThread(std::addressof(this->detector_thread), DetectorThreadEntry, this, this->detector_thread_stack, sizeof(this->detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector)); + os::SetThreadNamePointer(std::addressof(this->detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector)); + os::StartThread(std::addressof(this->detector_thread)); + } + + void DeviceDetector::Finalize() { + /* Ensure we're not already finalized. */ + AMS_ABORT_UNLESS(this->state != State_Finalized); + + /* Signal event to end the detector thread. */ + os::SignalEvent(std::addressof(this->detector_thread_end_event)); + os::WaitThread(std::addressof(this->detector_thread)); + + /* Finalize thread and events. */ + os::DestroyThread(std::addressof(this->detector_thread)); + os::FinalizeEvent(std::addressof(this->ready_device_status_event)); + os::FinalizeEvent(std::addressof(this->request_sleep_wake_event)); + os::FinalizeEvent(std::addressof(this->acknowledge_sleep_awake_event)); + os::FinalizeEvent(std::addressof(this->detector_thread_end_event)); + } + + void DeviceDetector::PutToSleep() { + /* Signal request, wait for acknowledgement. */ + os::SignalEvent(std::addressof(this->request_sleep_wake_event)); + os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event)); + os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event)); + } + + void DeviceDetector::Awaken(bool force_det) { + /* Signal request, wait for acknowledgement. */ + this->force_detection = force_det; + os::SignalEvent(std::addressof(this->request_sleep_wake_event)); + os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event)); + os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event)); + } + + bool DeviceDetector::IsInserted() { + bool inserted = false; + + switch (this->state) { + case State_Initializing: + /* Wait for us to know whether the device is inserted. */ + os::WaitEvent(std::addressof(this->ready_device_status_event)); + [[fallthrough]]; + case State_Awake: + /* Get whether the device is currently inserted. */ + inserted = this->IsCurrentInserted(); + break; + case State_Sleep: + case State_Finalized: + /* Get whether the device was inserted when we last knew. */ + inserted = this->is_prev_inserted; + break; + } + + return inserted; + } + + void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) { + this->device_detection_event_callback_arg = arg; + this->device_detection_event_callback = cb; + } + + void DeviceDetector::UnregisterDetectionEventCallback() { + this->device_detection_event_callback = nullptr; + } + +} + +#endif diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.hpp new file mode 100644 index 0000000..a61f050 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.hpp @@ -0,0 +1,99 @@ +/* + * 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 . + */ +#pragma once +#include +#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) +#include + +namespace ams::sdmmc::impl { + + using InsertedCallback = void (*)(void *); + using RemovedCallback = void (*)(void *); + + struct CallbackInfo { + InsertedCallback inserted_callback; + void *inserted_callback_arg; + RemovedCallback removed_callback; + void *removed_callback_arg; + }; + + class DeviceDetector { + private: + enum State { + State_Initializing = 0, + State_Awake = 1, + State_Sleep = 2, + State_Finalized = 3, + }; + private: + alignas(os::ThreadStackAlignment) u8 detector_thread_stack[8_KB]; + State state; + bool is_prev_inserted; + bool force_detection; + os::ThreadType detector_thread; + os::EventType detector_thread_end_event; + os::EventType request_sleep_wake_event; + os::EventType acknowledge_sleep_awake_event; + os::EventType ready_device_status_event; + + DeviceCode gpio_device_code; + gpio::GpioValue inserted_gpio_value; + u32 gpio_debounce_ms; + gpio::GpioPadSession gpio_pad_session; + + CallbackInfo callback_info; + + DeviceDetectionEventCallback device_detection_event_callback; + void *device_detection_event_callback_arg; + private: + static void DetectorThreadEntry(void *arg) { + reinterpret_cast(arg)->DetectorThread(); + } + + void DetectorThread(); + bool IsCurrentInserted(); + void HandleDeviceStatus(bool prev_inserted, bool cur_inserted); + public: + explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd) + : gpio_device_code(dc), inserted_gpio_value(igv), gpio_debounce_ms(gd) + { + this->state = State_Finalized; + this->is_prev_inserted = false; + this->force_detection = false; + this->callback_info = {}; + this->device_detection_event_callback = nullptr; + this->device_detection_event_callback_arg = nullptr; + } + + void Initialize(CallbackInfo *ci); + void Finalize(); + + void PutToSleep(); + void Awaken(bool force_det); + + u32 GetDebounceMilliSeconds() const { + return this->gpio_debounce_ms; + } + + bool IsInserted(); + + void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg); + void UnregisterDetectionEventCallback(); + }; + +} + +#endif \ No newline at end of file diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.cpp new file mode 100644 index 0000000..a1ebe02 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.cpp @@ -0,0 +1,352 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_gc_asic_device_accessor.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + #define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(this->gc_asic_device.device_mutex) + + #else + + #define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() + + #endif + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + + #define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!this->gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved()) + + #else + + #define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() + + #endif + + Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const { + /* Validate the operation buffer. */ + AMS_ABORT_UNLESS(op_buf != nullptr); + AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize); + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false); + TransferData xfer_data(const_cast(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data)); + if (R_FAILED(result)) { + /* We failed to write operation. Check if we were removed. */ + AMS_SDMMC_CHECK_GC_ASIC_REMOVED(); + + /* Determine what result we should return. */ + Result return_result = result; + { + /* Issue a stop transmission command. */ + u32 resp = 0; + result = hc->IssueStopTransmissionCommand(std::addressof(resp)); + if (R_SUCCEEDED(result)) { + /* If we successfully stopped transmission but have an error status, we prefer to return that. */ + result = this->gc_asic_device.CheckDeviceStatus(resp); + if (R_FAILED(result)) { + return_result = result; + } + } + + /* Check again if we were removed. */ + AMS_SDMMC_CHECK_GC_ASIC_REMOVED(); + + /* Request device status. */ + u32 device_status; + result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0); + + /* If we got a device status error here and we didn't previously, we prefer to return that. */ + if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) { + return_result = result; + } + } + return return_result; + } + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->gc_asic_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran)); + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::IssueCommandSleep() { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran)); + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran)); + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::StartupGcAsicDevice() { + /* Start up the host controller. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false)); + + /* Wait 10 clocks for configuration to take. */ + WaitClocks(10, hc->GetDeviceClockFrequencyKHz()); + + /* Perform tuning with command index 21. */ + AMS_ABORT_UNLESS(hc->IsSupportedTuning()); + R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21)); + + /* Set the device as low capacity/no memory. */ + this->gc_asic_device.SetHighCapacity(false); + this->gc_asic_device.SetMemoryCapacity(0); + + /* Enable power saving. */ + hc->SetPowerSaving(true); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::OnActivate() { + /* If we fail to start up the device, ensure the host controller is shut down. */ + auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); }; + + /* Try to start up the device. */ + R_TRY(this->StartupGcAsicDevice()); + + /* We started up, so we don't need to power down. */ + power_guard.Cancel(); + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { + /* Check that we're not performing zero-byte rw. */ + AMS_ABORT_UNLESS(num_sectors > 0); + + /* Check that the buffer is big enough for the rw. */ + AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors); + + /* Perform the read/write. */ + u32 num_transferred_blocks; + R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read)); + + /* Require that we read/wrote as many sectors as we expected. */ + AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors); + + return ResultSuccess(); + } + + void GcAsicDeviceAccessor::Initialize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* If we've already initialized, we don't need to do anything. */ + if (this->is_initialized) { + return; + } + + /* Set the base device to our gc asic device. */ + BaseDeviceAccessor::SetDevice(std::addressof(this->gc_asic_device)); + + /* Initialize. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + this->gc_asic_device.InitializeRemovedEvent(); + hc->PreSetRemovedEvent(this->gc_asic_device.GetRemovedEvent()); + } + #endif + hc->Initialize(); + + /* Mark ourselves as initialized. */ + this->is_initialized = true; + } + + void GcAsicDeviceAccessor::Finalize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* If we've already finalized, we don't need to do anything. */ + if (!this->is_initialized) { + return; + } + this->is_initialized = false; + + /* Deactivate the device. */ + BaseDeviceAccessor::Deactivate(); + + /* Finalize the host controller. */ + BaseDeviceAccessor::GetHostController()->Finalize(); + + /* Finalize the removed event. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + this->gc_asic_device.FinalizeRemovedEvent(); + } + #endif + } + + Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { + /* Check that we can write to output. */ + AMS_ABORT_UNLESS(out_speed_mode != nullptr); + + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + *out_speed_mode = SpeedMode_GcAsicSpeed; + return ResultSuccess(); + } + + void GcAsicDeviceAccessor::PutGcAsicToSleep() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* If the device isn't awake, we don't need to do anything. */ + if (!this->gc_asic_device.IsAwake()) { + return; + } + + /* If necessary, put the host controller to sleep. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved()) + #else + if (this->gc_asic_device.IsActive()) + #endif + { + BaseDeviceAccessor::GetHostController()->PutToSleep(); + } + + /* Put the gc asic device to sleep. */ + this->gc_asic_device.PutToSleep(); + } + + Result GcAsicDeviceAccessor::AwakenGcAsic() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* If the device is awake, we don't need to do anything. */ + R_SUCCEED_IF(this->gc_asic_device.IsAwake()); + + /* Wake the device. */ + this->gc_asic_device.Awaken(); + + /* Wake the host controller, if we need to.*/ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved()) + #else + if (this->gc_asic_device.IsActive()) + #endif + { + R_TRY(BaseDeviceAccessor::GetHostController()->Awaken()); + } + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + /* Issue the command. */ + R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::FinishGcAsicOperation() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + /* Issue the command. */ + R_TRY(this->IssueCommandFinishOperation()); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::AbortGcAsicOperation() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + /* Issue stop transmission command. */ + u32 resp = 0; + R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp))); + R_TRY(this->gc_asic_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::SleepGcAsic() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + /* Issue the command. */ + R_TRY(this->IssueCommandSleep()); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result GcAsicDeviceAccessor::UpdateGcAsicKey() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->gc_asic_device.CheckAccessible()); + + /* Issue the command. */ + R_TRY(this->IssueCommandUpdateKey()); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.hpp new file mode 100644 index 0000000..c6c24ec --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_gc_asic_device_accessor.hpp @@ -0,0 +1,96 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_base_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + class GcAsicDevice : public BaseDevice { + private: + static constexpr u16 Rca = 0; + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + mutable os::EventType removed_event; + #endif + public: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual os::EventType *GetRemovedEvent() const override { + return std::addressof(this->removed_event); + } + #endif + + virtual DeviceType GetDeviceType() const override { + return DeviceType_GcAsic; + } + + virtual u16 GetRca() const override { + return Rca; + } + }; + + class GcAsicDeviceAccessor : public BaseDeviceAccessor { + private: + GcAsicDevice gc_asic_device; + bool is_initialized; + private: + Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const; + Result IssueCommandFinishOperation() const; + Result IssueCommandSleep(); + Result IssueCommandUpdateKey() const; + Result StartupGcAsicDevice(); + protected: + virtual Result OnActivate() override; + virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; + + virtual Result ReStartup() override { + AMS_ABORT("Can't ReStartup GcAsic\n"); + } + public: + virtual void Initialize() override; + virtual void Finalize() override; + virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; + public: + explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), is_initialized(false) { + /* ... */ + } + + void PutGcAsicToSleep(); + Result AwakenGcAsic(); + Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size); + Result FinishGcAsicOperation(); + Result AbortGcAsicOperation(); + Result SleepGcAsic(); + Result UpdateGcAsicKey(); + + void SignalGcRemovedEvent() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + this->gc_asic_device.SignalRemovedEvent(); + #else + AMS_ABORT("SignalGcRemovedEvent called without event support\n"); + #endif + } + + void ClearGcRemovedEvent() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + this->gc_asic_device.ClearRemovedEvent(); + #else + AMS_ABORT("ClearGcRemovedEvent called without event support\n"); + #endif + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_device_accessor.hpp new file mode 100644 index 0000000..94e85bd --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_device_accessor.hpp @@ -0,0 +1,49 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" + +namespace ams::sdmmc::impl { + + class IDeviceAccessor { + public: + virtual void Initialize() = 0; + virtual void Finalize() = 0; + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; + virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; + #endif + + virtual Result Activate() = 0; + virtual void Deactivate() = 0; + + virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0; + virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0; + + virtual Result GetSpeedMode(SpeedMode *out) const = 0; + virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0; + virtual Result GetDeviceStatus(u32 *out) const = 0; + virtual Result GetOcr(u32 *out) const = 0; + virtual Result GetRca(u16 *out) const = 0; + virtual Result GetCid(void *out, size_t size) const = 0; + virtual Result GetCsd(void *out, size_t size) const = 0; + + virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_host_controller.hpp new file mode 100644 index 0000000..f4439bb --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_host_controller.hpp @@ -0,0 +1,196 @@ +/* + * 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 . + */ +#pragma once +#include + +#if defined(AMS_SDMMC_USE_OS_EVENTS) +#include +#endif + +namespace ams::sdmmc::impl { + + enum ResponseType { + ResponseType_R0 = 0, + ResponseType_R1 = 1, + ResponseType_R2 = 2, + ResponseType_R3 = 3, + ResponseType_R6 = 4, + ResponseType_R7 = 5, + }; + + enum TransferDirection { + TransferDirection_ReadFromDevice = 0, + TransferDirection_WriteToDevice = 1, + }; + + enum CommandIndex { + /* Generic commands. */ + CommandIndex_GoIdleState = 0, + CommandIndex_SendOpCond = 1, + CommandIndex_AllSendCid = 2, + CommandIndex_SendRelativeAddr = 3, + CommandIndex_SetRelativeAddr = 3, + CommandIndex_SetDsr = 4, + + CommandIndex_Switch = 6, + CommandIndex_SelectCard = 7, + CommandIndex_DeselectCard = 7, + CommandIndex_SendIfCond = 8, + CommandIndex_SendExtCsd = 8, + CommandIndex_SendCsd = 9, + CommandIndex_SendCid = 10, + CommandIndex_VoltageSwitch = 11, + CommandIndex_StopTransmission = 12, + CommandIndex_SendStatus = 13, + CommandIndex_SendTaskStatus = 13, + + CommandIndex_GoInactiveState = 15, + CommandIndex_SetBlockLen = 16, + CommandIndex_ReadSingleBlock = 17, + CommandIndex_ReadMultipleBlock = 18, + CommandIndex_SendTuningBlock = 19, + CommandIndex_SpeedClassControl = 20, + + CommandIndex_AddressExtension = 22, + CommandIndex_SetBlockCount = 23, + CommandIndex_WriteBlock = 24, + CommandIndex_WriteMultipleBlock = 25, + + CommandIndex_ProgramCsd = 27, + CommandIndex_SetWriteProt = 28, + CommandIndex_ClearWriteProt = 29, + CommandIndex_SendWriteProt = 30, + + CommandIndex_EraseWriteBlockStart = 32, + CommandIndex_EraseWriteBlockEnd = 33, + + CommandIndex_EraseGroupStart = 35, + CommandIndex_EraseGroupEnd = 36, + + CommandIndex_Erase = 38, + + CommandIndex_LockUnlock = 42, + + CommandIndex_AppCmd = 55, + CommandIndex_GenCmd = 56, + + /* Nintendo specific vendor commands for lotus3. */ + CommandIndex_GcAsicWriteOperation = 60, + CommandIndex_GcAsicFinishOperation = 61, + CommandIndex_GcAsicSleep = 62, + CommandIndex_GcAsicUpdateKey = 63, + }; + + struct Command { + u32 command_index; + u32 command_argument; + ResponseType response_type; + bool is_busy; + + constexpr Command(u32 ci, u32 ca, ResponseType r, bool b) : command_index(ci), command_argument(ca), response_type(r), is_busy(b) { /* ... */ } + }; + + struct TransferData { + void *buffer; + size_t block_size; + u32 num_blocks; + TransferDirection transfer_direction; + bool is_multi_block_transfer; + bool is_stop_transmission_command_enabled; + + constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd, bool mb, bool st) + : buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(mb), is_stop_transmission_command_enabled(st) + { + if (this->num_blocks > 1) { + AMS_ABORT_UNLESS(this->is_multi_block_transfer); + } + } + + constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd) + : buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(false), is_stop_transmission_command_enabled(false) + { + AMS_ABORT_UNLESS(this->num_blocks == 1); + } + }; + + class IHostController { + public: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual void PreSetRemovedEvent(ams::os::EventType *event) = 0; + #endif + + virtual void Initialize() = 0; + virtual void Finalize() = 0; + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; + virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; + #endif + + virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0; + + virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0; + virtual void Shutdown() = 0; + virtual void PutToSleep() = 0; + virtual Result Awaken() = 0; + + virtual Result SwitchToSdr12(); + + virtual bool IsSupportedBusPower(BusPower bus_power) const = 0; + virtual BusPower GetBusPower() const = 0; + + virtual bool IsSupportedBusWidth(BusWidth bus_width) const = 0; + virtual void SetBusWidth(BusWidth bus_width) = 0; + virtual BusWidth GetBusWidth() const = 0; + + virtual Result SetSpeedMode(SpeedMode speed_mode) = 0; + virtual SpeedMode GetSpeedMode() const = 0; + + virtual u32 GetDeviceClockFrequencyKHz() const = 0; + + virtual void SetPowerSaving(bool en) = 0; + virtual bool IsPowerSavingEnable() const = 0; + + virtual void EnableDeviceClock() = 0; + virtual void DisableDeviceClock() = 0; + virtual bool IsDeviceClockEnable() const = 0; + + virtual u32 GetMaxTransferNumBlocks() const = 0; + + virtual void ChangeCheckTransferInterval(u32 ms) = 0; + virtual void SetDefaultCheckTransferInterval() = 0; + + virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) = 0; + virtual Result IssueStopTransmissionCommand(u32 *out_response) = 0; + + ALWAYS_INLINE Result IssueCommand(const Command *command, TransferData *xfer_data) { + return this->IssueCommand(command, xfer_data, nullptr); + } + + ALWAYS_INLINE Result IssueCommand(const Command *command) { + return this->IssueCommand(command, nullptr, nullptr); + } + + virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const = 0; + virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const = 0; + + virtual bool IsSupportedTuning() const = 0; + virtual Result Tuning(SpeedMode speed_mode, u32 command_index) = 0; + virtual void SaveTuningStatusForHs400() = 0; + virtual Result GetInternalStatus() const = 0; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.cpp new file mode 100644 index 0000000..bf36180 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.cpp @@ -0,0 +1,998 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_timer.hpp" +#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" +#include "sdmmc_io_impl.board.nintendo_nx.hpp" + +namespace ams::sdmmc::impl { + + /* Lovingly taken from libexosphere. */ + namespace i2c_impl { + + enum Port { + Port_1 = 0, + /* TODO: Support other ports? */ + Port_5 = 4, + + Port_Count = 5, + }; + + constexpr inline const dd::PhysicalAddress I2c5RegistersAddress = UINT64_C(0x7000D000); + constexpr inline const size_t I2c5RegistersSize = 4_KB; + + namespace { + + constexpr inline size_t MaxTransferSize = sizeof(u32); + + constinit std::array g_register_addresses = [] { + std::array arr = {}; + return arr; + }(); + + void LoadConfig(uintptr_t address) { + /* Configure for TIMEOUT and MSTR config load. */ + /* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */ + /* We will reproduce the write just in case it is undocumented. */ + reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1), + I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Wait up to 20 microseconds for the master config to be loaded. */ + for (int i = 0; i < 20; ++i) { + if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) { + return; + } + util::WaitMicroSeconds(1); + } + } + + void ClearBus(uintptr_t address) { + /* Configure the bus clear register. */ + reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + /* Load the config. */ + LoadConfig(address); + + /* Wait up to 250us (in 25 us increments) until the bus clear is done. */ + for (int i = 0; i < 10; ++i) { + if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) { + break; + } + + util::WaitMicroSeconds(25); + } + + /* Read the bus clear status. */ + reg::Read(address + I2C_BUS_CLEAR_STATUS); + } + + void InitializePort(uintptr_t address) { + /* Calculate the divisor. */ + constexpr int Divisor = util::DivideUp(19200, 8 * 400); + + /* Set the divisor. */ + reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1), + I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1)); + + /* Clear the bus. */ + ClearBus(address); + + /* Clear the status. */ + reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER)); + } + + bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { + AMS_UNUSED(port, unused); + + /* Ensure we don't write too much. */ + u32 data = 0; + if (src_size > MaxTransferSize) { + return false; + } + + /* Copy the data to a transfer word. */ + std::memcpy(std::addressof(data), src, src_size); + + + /* Configure the to write the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE)); + + /* Configure to write the data. */ + reg::Write(base_address + I2C_I2C_CMD_DATA1, data); + + /* Configure to write the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check if the transfer was successful. */ + return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL)); + } + + bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { + AMS_UNUSED(port, unused); + + /* Ensure we don't read too much. */ + if (dst_size > MaxTransferSize) { + return false; + } + + /* Configure the to read the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ)); + + /* Configure to read the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check that the transfer was successful. */ + if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) { + return false; + } + + /* Read and copy out the data. */ + u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1); + std::memcpy(dst, std::addressof(data), dst_size); + return true; + } + + } + + void SetRegisterAddress(Port port, uintptr_t address) { + g_register_addresses[port] = address; + } + + void Initialize(Port port) { + InitializePort(g_register_addresses[port]); + } + + bool Query(void *dst, size_t dst_size, Port port, int address, int r) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Select the register we want to read. */ + bool success = Write(base_address, port, address, std::addressof(r), 1, false); + if (success) { + /* If we successfully selected, read data from the register. */ + success = Read(base_address, port, dst, dst_size, address, true); + } + + return success; + } + + bool Send(Port port, int address, int r, const void *src, size_t src_size) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Create a transfer buffer, make sure we can use it. */ + u8 buffer[MaxTransferSize]; + if (src_size > sizeof(buffer) - 1) { + return false; + } + + /* Copy data into the buffer. */ + buffer[0] = static_cast(r); + std::memcpy(buffer + 1, src, src_size); + + return Write(base_address, port, address, buffer, src_size + 1, false); + } + + } + + namespace max7762x { + + /* The only regulator we care about for SD card power is ldo2. */ + constexpr inline const u8 Max77620PwrI2cAddr = 0x3C; + + constexpr inline const u32 Max77620Ldo2MvStep = 50'000; /* 50 mV (50K uV) steps. */ + constexpr inline const u32 Max77620Ldo2MvMin = 800'000; /* 0.8V min voltage. */ + constexpr inline const u32 Max77620Ldo2MvDefault = 1'800'000; /* 1.8V default voltage. */ + constexpr inline const u32 Max77620Ldo2MvMax = 3'300'000; /* 3.3V max voltage. */ + constexpr inline const u8 Max77620Ldo2VoltAddr = 0x27; + constexpr inline const u8 Max77620Ldo2CfgAddr = 0x28; + constexpr inline const u8 Max77620Ldo2VoltMask = 0x3F; + constexpr inline const u8 Max77620Ldo2EnableMask = 0xC0; + constexpr inline const u8 Max77620Ldo2EnableShift = 0x6; + constexpr inline const u8 Max77620Ldo2StatusMask = 0x00; + + namespace { + + #if defined(AMS_SDMMC_THREAD_SAFE) + constinit os::SdkMutex g_i2c_init_mutex; + + #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex) + + #else + + #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() + + #endif + + constinit bool g_initialized_i2c = false; + + void EnsureI2cInitialized() { + if (AMS_UNLIKELY(!g_initialized_i2c)) { + /* Ensure we have exclusive access to the i2c init status. */ + AMS_SDMMC_LOCK_I2C_INIT_MUTEX(); + + if (AMS_LIKELY(!g_initialized_i2c)) { + i2c_impl::SetRegisterAddress(i2c_impl::Port_5, dd::QueryIoMapping(i2c_impl::I2c5RegistersAddress, i2c_impl::I2c5RegistersSize)); + i2c_impl::Initialize(i2c_impl::Port_5); + g_initialized_i2c = true; + } + } + } + + } + + bool SetVoltageEnabled(bool en) { + /* Ensure that we can use i2c to communicate with the max7762x regulator. */ + EnsureI2cInitialized(); + + /* Read the current value. */ + u8 val; + if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { + return false; + } + + /* Set or clear the enable mask. */ + val &= ~Max77620Ldo2EnableMask; + if (en) { + val |= ((3 << Max77620Ldo2EnableShift) & Max77620Ldo2EnableMask); + } + + /* Write the updated value. */ + if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { + return false; + } + + /* Wait 1ms for change to take. */ + WaitMicroSeconds(1); + + /* Voltage is now enabled/disabled. */ + return true; + } + + bool SetVoltageValue(u32 micro_volts) { + /* Ensure that we can use i2c to communicate with the max7762x regulator. */ + EnsureI2cInitialized(); + + /* Check that the value is within range. */ + if (micro_volts < Max77620Ldo2MvMin || Max77620Ldo2MvMax < micro_volts) { + return false; + } + + /* Determine the mult. */ + const u32 mult = util::DivideUp(micro_volts - Max77620Ldo2MvMin, Max77620Ldo2MvStep); + + /* Read the current value. */ + u8 val; + if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { + return false; + } + + /* Set the new voltage. */ + val &= ~Max77620Ldo2VoltMask; + val |= (mult & Max77620Ldo2VoltMask); + + /* Write the updated value. */ + if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { + return false; + } + + /* Wait 1ms for change to take. */ + WaitMicroSeconds(1); + + /* Voltage is now set. */ + return true; + } + + } + + Result SetSdCardVoltageEnabled(bool en) { + /* TODO: A way for this to be non-fatal? */ + AMS_ABORT_UNLESS(max7762x::SetVoltageEnabled(en)); + + return ResultSuccess(); + } + + Result SetSdCardVoltageValue(u32 micro_volts) { + /* TODO: A way for this to be non-fatal? */ + AMS_ABORT_UNLESS(max7762x::SetVoltageValue(micro_volts)); + + return ResultSuccess(); + } + + namespace gpio_impl { + + namespace { + + constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000d000; + constexpr inline size_t GpioRegistersSize = 4_KB; + + enum GpioPadPort { + GpioPadPort_A = 0, + GpioPadPort_B = 1, + GpioPadPort_C = 2, + GpioPadPort_D = 3, + GpioPadPort_E = 4, + GpioPadPort_F = 5, + GpioPadPort_G = 6, + GpioPadPort_H = 7, + GpioPadPort_I = 8, + GpioPadPort_J = 9, + GpioPadPort_K = 10, + GpioPadPort_L = 11, + GpioPadPort_M = 12, + GpioPadPort_N = 13, + GpioPadPort_O = 14, + GpioPadPort_P = 15, + GpioPadPort_Q = 16, + GpioPadPort_R = 17, + GpioPadPort_S = 18, + GpioPadPort_T = 19, + GpioPadPort_U = 20, + GpioPadPort_V = 21, + GpioPadPort_W = 22, + GpioPadPort_X = 23, + GpioPadPort_Y = 24, + GpioPadPort_Z = 25, + GpioPadPort_AA = 26, + GpioPadPort_BB = 27, + GpioPadPort_CC = 28, + GpioPadPort_DD = 29, + GpioPadPort_EE = 30, + GpioPadPort_FF = 31, + }; + + consteval unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { + AMS_ASSUME(which < 8); + + return (static_cast(port) * 8) + which; + } + + enum InternalGpioPadNumber { + InternalGpioPadNumber_E4 = GetInternalGpioPadNumber(GpioPadPort_E, 4), + InternalGpioPadNumber_M0 = GetInternalGpioPadNumber(GpioPadPort_M, 0), + }; + + constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { + return (number >> 5); + } + + constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { + return (number >> 3); + } + + constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { + return (number & 7); + } + + constexpr int ConvertPortNumberToOffset(int port_number) { + return (port_number & 3); + } + + struct PadNameToInternalPadNumberEntry { + GpioPadName pad_name; + InternalGpioPadNumber internal_number; + }; + + constexpr inline const PadNameToInternalPadNumberEntry PadNameToInternalPadNumberTable[] = { + { GpioPadName_PowSdEn, InternalGpioPadNumber_E4 }, + }; + + constexpr InternalGpioPadNumber ConvertPadNameToInternalPadNumber(GpioPadName pad) { + const PadNameToInternalPadNumberEntry *target = nullptr; + for (const auto &entry : PadNameToInternalPadNumberTable) { + if (entry.pad_name == pad) { + target = std::addressof(entry); + break; + } + } + + AMS_ABORT_UNLESS(target != nullptr); + return target->internal_number; + } + + enum GpioRegisterType { + GpioRegisterType_GPIO_CNF = 0, + GpioRegisterType_GPIO_OE = 1, + GpioRegisterType_GPIO_OUT = 2, + GpioRegisterType_GPIO_IN = 3, + GpioRegisterType_GPIO_INT_STA = 4, + GpioRegisterType_GPIO_INT_ENB = 5, + GpioRegisterType_GPIO_INT_LVL = 6, + GpioRegisterType_GPIO_INT_CLR = 7, + GpioRegisterType_GPIO_DB_CTRL = 8, + GpioRegisterType_GPIO_DB_CNT = 9, + }; + + constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; + constexpr inline int MaskedWriteBitOffset = 8; + + constexpr uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { + const auto controller = ConvertInternalGpioPadNumberToController(pad_number); + const auto port = ConvertInternalGpioPadNumberToPort(pad_number); + const auto offset = ConvertPortNumberToOffset(port); + + switch (reg_type) { + default: + return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CTRL: + return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CNT: + return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); + } + } + + void SetMaskedBit(uintptr_t pad_address, int index, int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); + } + + void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); + } + + } + + void OpenSession(GpioPadName pad) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad as GPIO by setting the appropriate bit in CNF. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_CNF, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, 1); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + void CloseSession(GpioPadName pad) { + /* Nothing needs to be done here, as the only thing official code does is unbind the interrupt event. */ + AMS_UNUSED(pad); + } + + void SetDirection(GpioPadName pad, Direction direction) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad direction modifying the appropriate bit in OE. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, direction); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + void SetValue(GpioPadName pad, GpioValue value) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad value modifying the appropriate bit in OUT. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OUT, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, value); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + } + + namespace pinmux_impl { + + namespace { + + constexpr auto Sdmmc1ClkCmdDat03PadNumber = gpio_impl::InternalGpioPadNumber_M0; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadMask = 0x3F; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfGpio = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfSfio = 0x00; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadOutHigh = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadOutLow = 0x00; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadOeOutput = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadOeInput = 0x00; + + struct PinmuxDefinition { + u32 reg_offset; + u32 mask_val; + u32 pm_val; + }; + + /* NOTE: We only use the SDMMC1 pins, which are conveniently the first few... */ + constexpr const PinmuxDefinition PinmuxDefinitionMap[] = { + {0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */ + {0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */ + {0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */ + {0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */ + {0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */ + {0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */ + }; + + enum PinmuxPadIndex { + PinmuxPadIndex_Sdmmc1Clk = 0, + PinmuxPadIndex_Sdmmc1Cmd = 1, + PinmuxPadIndex_Sdmmc1Dat3 = 2, + PinmuxPadIndex_Sdmmc1Dat2 = 3, + PinmuxPadIndex_Sdmmc1Dat1 = 4, + PinmuxPadIndex_Sdmmc1Dat0 = 5, + + PinmuxPadIndex_Count, + }; + + static_assert(util::size(PinmuxDefinitionMap) == PinmuxPadIndex_Count); + + consteval const PinmuxDefinition GetDefinition(PinmuxPadIndex pad_index) { + AMS_ABORT_UNLESS(pad_index < PinmuxPadIndex_Count); + + return PinmuxDefinitionMap[pad_index]; + } + + template + ALWAYS_INLINE u32 UpdatePinmuxPad(uintptr_t pinmux_base_vaddr) { + constexpr const PinmuxDefinition Definition = GetDefinition(PadIndex); + + /* Fetch this PINMUX's register offset */ + constexpr u32 PinmuxRegOffset = Definition.reg_offset; + + /* Fetch this PINMUX's mask value */ + constexpr u32 PinmuxMaskVal = Definition.mask_val; + + /* Get current register ptr. */ + const uintptr_t pinmux_reg = pinmux_base_vaddr + PinmuxRegOffset; + + /* Read from the PINMUX register */ + u32 pinmux_val = reg::Read(pinmux_reg); + + /* This PINMUX register is locked */ + AMS_ABORT_UNLESS((pinmux_val & 0x80) == 0); + + constexpr u32 PmVal = (PinmuxConfigVal & 0x07); + + /* Adjust PM */ + if constexpr (PinmuxConfigMaskVal & 0x07) { + /* Apply additional changes first */ + if constexpr (PmVal == 0x05) { + /* This pin supports PUPD change */ + if constexpr (PinmuxMaskVal & 0x0C) { + /* Change PUPD */ + if ((pinmux_val & 0x0C) != 0x04) { + pinmux_val &= 0xFFFFFFF3; + pinmux_val |= 0x04; + } + } + + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (!(pinmux_val & 0x10)) { + pinmux_val |= 0x10; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (pinmux_val & 0x40) { + pinmux_val &= 0xFFFFFFBF; + } + } + } + + /* Translate PM value if necessary */ + constexpr u32 TranslatedPmVal = (PmVal == 0x04 || PmVal == 0x05 || PmVal >= 0x06) ? Definition.pm_val : PmVal; + + /* This pin supports PM change */ + if constexpr (PinmuxMaskVal & 0x03) { + /* Change PM */ + if ((pinmux_val & 0x03) != (TranslatedPmVal & 0x03)) { + pinmux_val &= 0xFFFFFFFC; + pinmux_val |= (TranslatedPmVal & 0x03); + } + } + } + + constexpr u32 PupdConfigVal = (PinmuxConfigVal & 0x18); + + /* Adjust PUPD */ + if constexpr (PinmuxConfigMaskVal & 0x18) { + if constexpr (PupdConfigVal < 0x11) { + /* This pin supports PUPD change */ + if constexpr (PinmuxMaskVal & 0x0C) { + /* Change PUPD */ + if (((pinmux_val >> 0x02) & 0x03) != (PupdConfigVal >> 0x03)) { + pinmux_val &= 0xFFFFFFF3; + pinmux_val |= (PupdConfigVal >> 0x01); + } + } + } + } + + constexpr u32 EodConfigVal = (PinmuxConfigVal & 0x60); + + /* Adjust EOd field */ + if constexpr (PinmuxConfigMaskVal & 0x60) { + if constexpr (EodConfigVal == 0x20) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (!(pinmux_val & 0x10)) { + pinmux_val |= 0x10; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } else if constexpr (EodConfigVal == 0x40) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } else if constexpr (EodConfigVal == 0x60) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (!(pinmux_val & 0x800)) { + pinmux_val |= 0x800; + } + } + } else { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (pinmux_val & 0x40) { + pinmux_val &= 0xFFFFFFBF; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } + } + + constexpr u32 LockConfigVal = (PinmuxConfigVal & 0x80); + + /* Adjust Lock */ + if constexpr (PinmuxConfigMaskVal & 0x80) { + /* This pin supports Lock change */ + if constexpr (PinmuxMaskVal & 0x80) { + /* Change Lock */ + if ((pinmux_val ^ PinmuxConfigVal) & 0x80) { + pinmux_val &= 0xFFFFFF7F; + pinmux_val |= LockConfigVal; + } + } + } + + constexpr u32 IoResetConfigVal = (((PinmuxConfigVal >> 0x08) & 0x1) << 0x10); + + /* Adjust IoReset */ + if constexpr (PinmuxConfigMaskVal & 0x100) { + /* This pin supports IoReset change */ + if constexpr (PinmuxMaskVal & 0x10000) { + /* Change IoReset */ + if (((pinmux_val >> 0x10) ^ (PinmuxConfigVal >> 0x08)) & 0x01) { + pinmux_val &= 0xFFFEFFFF; + pinmux_val |= IoResetConfigVal; + } + } + } + + constexpr u32 ParkConfigVal = (((PinmuxConfigVal >> 0x0A) & 0x1) << 0x5); + + /* Adjust Park */ + if constexpr (PinmuxConfigMaskVal & 0x400) { + /* This pin supports Park change */ + if constexpr (PinmuxMaskVal & 0x20) { + /* Change Park */ + if (((pinmux_val >> 0x05) ^ (PinmuxConfigVal >> 0x0A)) & 0x01) { + pinmux_val &= 0xFFFFFFDF; + pinmux_val |= ParkConfigVal; + } + } + } + + constexpr u32 ElpdrConfigVal = (((PinmuxConfigVal >> 0x0B) & 0x1) << 0x08); + + /* Adjust ELpdr */ + if constexpr (PinmuxConfigMaskVal & 0x800) { + /* This pin supports ELpdr change */ + if constexpr (PinmuxMaskVal & 0x100) { + /* Change ELpdr */ + if (((pinmux_val >> 0x08) ^ (PinmuxConfigVal >> 0x0B)) & 0x01) { + pinmux_val &= 0xFFFFFEFF; + pinmux_val |= ElpdrConfigVal; + } + } + } + + constexpr u32 EhsmConfigVal = (((PinmuxConfigVal >> 0x0C) & 0x1) << 0x09); + + /* Adjust EHsm */ + if constexpr (PinmuxConfigMaskVal & 0x1000) { + /* This pin supports EHsm change */ + if constexpr (PinmuxMaskVal & 0x200) { + /* Change EHsm */ + if (((pinmux_val >> 0x09) ^ (PinmuxConfigVal >> 0x0C)) & 0x01) { + pinmux_val &= 0xFFFFFDFF; + pinmux_val |= EhsmConfigVal; + } + } + } + + constexpr u32 EIoHvConfigVal = (((PinmuxConfigVal >> 0x09) & 0x1) << 0x0A); + + /* Adjust EIoHv */ + if constexpr (PinmuxConfigMaskVal & 0x200) { + /* This pin supports EIoHv change */ + if constexpr (PinmuxMaskVal & 0x400) { + /* Change EIoHv */ + if (((pinmux_val >> 0x0A) ^ (PinmuxConfigVal >> 0x09)) & 0x01) { + pinmux_val &= 0xFFFFFBFF; + pinmux_val |= EIoHvConfigVal; + } + } + } + + constexpr u32 EschmtConfigVal = (((PinmuxConfigVal >> 0x0D) & 0x1) << 0x0C); + + /* Adjust ESchmt */ + if constexpr (PinmuxConfigMaskVal & 0x2000) { + /* This pin supports ESchmt change */ + if constexpr (PinmuxMaskVal & 0x1000) { + /* Change ESchmt */ + if (((pinmux_val >> 0x0C) ^ (PinmuxConfigVal >> 0x0D)) & 0x01) { + pinmux_val &= 0xFFFFEFFF; + pinmux_val |= EschmtConfigVal; + } + } + } + + constexpr u32 PreempConfigVal = (((PinmuxConfigVal >> 0x10) & 0x1) << 0xF); + + /* Adjust Preemp */ + if constexpr (PinmuxConfigMaskVal & 0x10000) { + /* This pin supports Preemp change */ + if constexpr (PinmuxMaskVal & 0x8000) { + /* Change Preemp */ + if (((pinmux_val >> 0x0F) ^ (PinmuxConfigVal >> 0x10)) & 0x01) { + pinmux_val &= 0xFFFF7FFF; + pinmux_val |= PreempConfigVal; + } + } + } + + constexpr u32 DrvTypeConfigVal = (((PinmuxConfigVal >> 0x0E) & 0x3) << 0xD); + + /* Adjust DrvType */ + if constexpr (PinmuxConfigMaskVal & 0xC000) { + /* This pin supports DrvType change */ + if constexpr (PinmuxMaskVal & 0x6000) { + /* Change DrvType */ + if (((pinmux_val >> 0x0D) ^ (PinmuxConfigVal >> 0x0E)) & 0x03) { + pinmux_val &= 0xFFFF9FFF; + pinmux_val |= DrvTypeConfigVal; + } + } + } + + /* Write to the appropriate PINMUX register */ + reg::Write(pinmux_reg, pinmux_val); + + /* Do a dummy read from the PINMUX register */ + pinmux_val = reg::Read(pinmux_reg); + + return pinmux_val; + } + + } + + void SetPinAssignment(PinAssignment assignment) { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + AMS_UNUSED(apb_address); + + /* Set the pin assignment. */ + switch (assignment) { + case PinAssignment_Sdmmc1OutputHigh: + { + /* Clear Sdmmc1Clk pulldown. */ + UpdatePinmuxPad(apb_address); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as gpio. */ + const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfGpio); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as high. */ + const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutHigh); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as output. */ + const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeOutput); + + /* Read to be sure that our configuration takes. */ + reg::Read(oe_address); + } + break; + case PinAssignment_Sdmmc1ResetState: + { + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as sfio. */ + const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfSfio); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as low. */ + const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutLow); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as input. */ + const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeInput); + + /* Read to be sure that our configuration takes. */ + reg::Read(oe_address); + + /* Set Sdmmc1Clk pulldown. */ + UpdatePinmuxPad(apb_address); + } + break; + case PinAssignment_Sdmmc1SchmtEnable: + { + /* Set Schmitt enable for all pins in the group. */ + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + } + break; + case PinAssignment_Sdmmc1SchmtDisable: + { + /* Set Schmitt disable for all pins in the group. */ + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.hpp new file mode 100644 index 0000000..89b6230 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_io_impl.board.nintendo_nx.hpp @@ -0,0 +1,62 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + Result SetSdCardVoltageEnabled(bool en); + Result SetSdCardVoltageValue(u32 micro_volts); + + namespace pinmux_impl { + + enum PinAssignment { + PinAssignment_Sdmmc1OutputHigh = 2, + PinAssignment_Sdmmc1ResetState = 3, + PinAssignment_Sdmmc1SchmtEnable = 4, + PinAssignment_Sdmmc1SchmtDisable = 5, + }; + + void SetPinAssignment(PinAssignment assignment); + + } + + namespace gpio_impl { + + enum GpioValue { + GpioValue_Low = 0, + GpioValue_High = 1 + }; + + enum Direction { + Direction_Input = 0, + Direction_Output = 1, + }; + + enum GpioPadName { + GpioPadName_PowSdEn = 2, + }; + + void OpenSession(GpioPadName pad); + void CloseSession(GpioPadName pad); + + void SetDirection(GpioPadName pad, Direction direction); + void SetValue(GpioPadName pad, GpioValue value); + + + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.cpp new file mode 100644 index 0000000..7468aab --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.cpp @@ -0,0 +1,728 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_mmc_device_accessor.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + #define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() std::scoped_lock lk(this->mmc_device.device_mutex) + + #else + + #define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() + + #endif + + namespace { + + constexpr inline u32 OcrCardPowerUpStatus = (1 << 31); + + constexpr inline u32 OcrAccessMode_Mask = (3 << 29); + constexpr inline u32 OcrAccessMode_SectorMode = (2 << 29); + + constexpr inline u8 ManufacturerId_Toshiba = 0x11; + + enum DeviceType : u8 { + DeviceType_HighSpeed26MHz = (1u << 0), + DeviceType_HighSpeed52MHz = (1u << 1), + DeviceType_HighSpeedDdr52MHz1_8VOr3_0V = (1u << 2), + DeviceType_HighSpeedDdr52MHz1_2V = (1u << 3), + DeviceType_Hs200Sdr200MHz1_8V = (1u << 4), + DeviceType_Hs200Sdr200MHz1_2V = (1u << 5), + DeviceType_Hs400Sdr200MHz1_8V = (1u << 6), + DeviceType_Hs400Sdr200MHz1_2V = (1u << 7), + }; + + constexpr bool IsToshibaMmc(const u8 *cid) { + /* Check whether the CID's manufacturer id field is Toshiba. */ + AMS_ABORT_UNLESS(cid != nullptr); + return cid[14] == ManufacturerId_Toshiba; + } + + constexpr bool IsLessThanSpecification4(const u8 *csd) { + const u8 spec_vers = ((csd[14] >> 2) & 0xF); + return spec_vers < 4; + } + + constexpr bool IsBkopAutoEnable(const u8 *ext_csd) { + /* Check the AUTO_EN bit of BKOPS_EN. */ + return (ext_csd[163] & (1u << 1)) != 0; + } + + constexpr u8 GetDeviceType(const u8 *ext_csd) { + /* Get the DEVICE_TYPE register. */ + AMS_ABORT_UNLESS(ext_csd != nullptr); + return ext_csd[196]; + } + + constexpr bool IsSupportedHs400(u8 device_type) { + return (device_type & DeviceType_Hs400Sdr200MHz1_8V) != 0; + } + + constexpr bool IsSupportedHs200(u8 device_type) { + return (device_type & DeviceType_Hs200Sdr200MHz1_8V) != 0; + } + + constexpr bool IsSupportedHighSpeed(u8 device_type) { + return (device_type & DeviceType_HighSpeed52MHz) != 0; + } + + constexpr u32 GetMemoryCapacityFromExtCsd(const u32 *ext_csd) { + /* Get the SEC_COUNT register. */ + AMS_ABORT_UNLESS(ext_csd != nullptr); + return ext_csd[212 / sizeof(u32)]; + } + + constexpr u32 GetBootPartitionMemoryCapacityFromExtCsd(const u8 *ext_csd) { + /* Get the BOOT_SIZE_MULT register. */ + AMS_ABORT_UNLESS(ext_csd != nullptr); + return ext_csd[226] * (128_KB / SectorSize); + } + + constexpr Result GetCurrentSpeedModeFromExtCsd(SpeedMode *out, const u8 *ext_csd) { + /* Get the HS_TIMING register. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(ext_csd != nullptr); + + switch (ext_csd[185] & 0xF) { + case 0: *out = SpeedMode_MmcLegacySpeed; break; + case 1: *out = SpeedMode_MmcHighSpeed; break; + case 2: *out = SpeedMode_MmcHs200; break; + case 3: *out = SpeedMode_MmcHs400; break; + default: return sdmmc::ResultUnexpectedMmcExtendedCsdValue(); + } + + return ResultSuccess(); + } + + } + + void MmcDevice::SetOcrAndHighCapacity(u32 ocr) { + /* Set ocr. */ + BaseDevice::SetOcr(ocr); + + /* Set high capacity. */ + BaseDevice::SetHighCapacity((ocr & OcrAccessMode_Mask) == OcrAccessMode_SectorMode); + } + + Result MmcDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const { + /* Get the command argument. */ + u32 arg = OcrAccessMode_SectorMode; + switch (bus_power) { + case BusPower_1_8V: arg |= 0x000080; break; + case BusPower_3_3V: arg |= 0x03F800; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R3; + Command command(CommandIndex_SendOpCond, arg, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + hc->GetLastResponse(out_ocr, sizeof(*out_ocr), CommandResponseType); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandSetRelativeAddr() const { + /* Get rca. */ + const u32 rca = this->mmc_device.GetRca(); + AMS_ABORT_UNLESS(rca > 0); + + /* Issue comamnd. */ + const u32 arg = rca << 16; + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_SetRelativeAddr, arg, false, DeviceState_Unknown)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandSwitch(CommandSwitch cs) const { + /* Get the command argument. */ + const u32 arg = GetCommandSwitchArgument(cs); + + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Switch, arg, true, DeviceState_Unknown)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandSendExtCsd(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= MmcExtendedCsdSize); + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_SendExtCsd, 0, CommandResponseType, false); + TransferData xfer_data(dst, MmcExtendedCsdSize, 1, TransferDirection_ReadFromDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->mmc_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandEraseGroupStart(u32 sector_index) const { + /* Get the command argument. */ + const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize; + + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupStart, arg, false, DeviceState_Unknown)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandEraseGroupEnd(u32 sector_index) const { + /* Get the command argument. */ + const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize; + + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupEnd, arg, false, DeviceState_Tran)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::IssueCommandErase() const { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Erase, 0, false, DeviceState_Tran)); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::CancelToshibaMmcModel() { + /* Special erase sequence done by Nintendo on Toshiba MMCs. */ + R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsProductionStateAwarenessEnable)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + R_TRY(this->IssueCommandSwitch(CommandSwitch_ClearBitsAutoModeEnable)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessNormal)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::ChangeToReadyState(BusPower bus_power) { + /* Be prepared to wait up to 1.5 seconds to change state. */ + ManualTimer timer(1500); + while (true) { + /* Get the ocr, and check if we're done. */ + u32 ocr; + R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), bus_power)); + if ((ocr & OcrCardPowerUpStatus) != 0) { + this->mmc_device.SetOcrAndHighCapacity(ocr); + return ResultSuccess(); + } + + /* Check if we've timed out. */ + R_UNLESS(timer.Update(), sdmmc::ResultMmcInitializationSoftwareTimeout()); + + /* Try again in 1ms. */ + WaitMicroSeconds(1000); + } + } + + Result MmcDeviceAccessor::ExtendBusWidth(BusWidth max_bw) { + /* If the maximum bus width is 1bit, we can't extend. */ + R_SUCCEED_IF(max_bw == BusWidth_1Bit); + + /* Determine what bus width to switch to. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + BusWidth target_bw = BusWidth_1Bit; + CommandSwitch cs; + if (max_bw == BusWidth_8Bit && hc->IsSupportedBusWidth(BusWidth_8Bit)) { + target_bw = BusWidth_8Bit; + cs = CommandSwitch_WriteBusWidth8Bit; + } else if ((max_bw == BusWidth_8Bit || max_bw == BusWidth_4Bit) && hc->IsSupportedBusWidth(BusWidth_4Bit)) { + target_bw = BusWidth_4Bit; + cs = CommandSwitch_WriteBusWidth4Bit; + } else { + /* Target bus width is 1bit. */ + return ResultSuccess(); + } + + /* Set the bus width. */ + R_TRY(this->IssueCommandSwitch(cs)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + hc->SetBusWidth(target_bw); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::EnableBkopsAuto() { + /* Issue the command. */ + R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsBkopsEnAutoEn)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::ChangeToHighSpeed(bool check_before) { + /* Issue high speed command. */ + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHighSpeed)); + + /* If we should check status before setting mode, do so. */ + if (check_before) { + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + } + + /* Set the host controller to high speed. */ + R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_MmcHighSpeed)); + + /* If we should check status after setting mode, do so. */ + if (!check_before) { + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + } + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::ChangeToHs200() { + /* Issue Hs200 command. */ + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs200)); + + /* Set the host controller to Hs200. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs200)); + + /* Perform tuning using command index 21. */ + R_TRY(hc->Tuning(SpeedMode_MmcHs200, 21)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::ChangeToHs400() { + /* Change first to Hs200. */ + R_TRY(this->ChangeToHs200()); + + /* Save tuning status. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + hc->SaveTuningStatusForHs400(); + + /* Change to high speed. */ + R_TRY(this->ChangeToHighSpeed(false)); + + /* Issue Hs400 command. */ + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteBusWidth8BitDdr)); + R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs400)); + + /* Set the host controller to Hs400. */ + R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs400)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::ExtendBusSpeed(u8 device_type, SpeedMode max_sm) { + /* We want to switch to the highest speed we can. */ + /* Check Hs400/Hs200 first. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + if (hc->IsSupportedTuning() && hc->GetBusPower() == BusPower_1_8V) { + if (hc->GetBusWidth() == BusWidth_8Bit && IsSupportedHs400(device_type) && max_sm == SpeedMode_MmcHs400) { + return this->ChangeToHs400(); + } else if ((hc->GetBusWidth() == BusWidth_8Bit || hc->GetBusWidth() == BusWidth_4Bit) && IsSupportedHs200(device_type) && (max_sm == SpeedMode_MmcHs400 || max_sm == SpeedMode_MmcHs200)) { + return this->ChangeToHs200(); + } + } + + /* Check if we can switch to high speed. */ + if (IsSupportedHighSpeed(device_type)) { + return this->ChangeToHighSpeed(true); + } + + /* We can't, so stay at normal speeds. */ + return ResultSuccess(); + } + + Result MmcDeviceAccessor::StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) { + /* Start up at an appropriate bus power. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + const BusPower bp = hc->IsSupportedBusPower(BusPower_1_8V) ? BusPower_1_8V : BusPower_3_3V; + R_TRY(hc->Startup(bp, BusWidth_1Bit, SpeedMode_MmcIdentification, false)); + + /* Wait 1ms for configuration to take. */ + WaitMicroSeconds(1000); + + /* Wait an additional 74 clocks for configuration to take. */ + WaitClocks(74, hc->GetDeviceClockFrequencyKHz()); + + /* Go to idle state. */ + R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState()); + this->current_partition = MmcPartition_UserData; + + /* Go to ready state. */ + R_TRY(this->ChangeToReadyState(bp)); + + /* Get the CID. */ + R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size)); + this->mmc_device.SetCid(wb, wb_size); + const bool is_toshiba = IsToshibaMmc(static_cast(wb)); + + /* Issue set relative addr. */ + R_TRY(this->IssueCommandSetRelativeAddr()); + + /* Get the CSD. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size)); + this->mmc_device.SetCsd(wb, wb_size); + const bool spec_under_4 = IsLessThanSpecification4(static_cast(wb)); + + /* Set the speed mode to legacy. */ + R_TRY(hc->SetSpeedMode(SpeedMode_MmcLegacySpeed)); + + /* Issue select card command. */ + R_TRY(BaseDeviceAccessor::IssueCommandSelectCard()); + + /* Set block length to sector size. */ + R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize()); + + /* If the device SPEC_VERS is less than 4, extended csd/switch aren't supported. */ + if (spec_under_4) { + R_TRY(this->mmc_device.SetLegacyMemoryCapacity()); + + this->mmc_device.SetActive(); + return ResultSuccess(); + } + + /* Extend the bus width to the largest that we can. */ + R_TRY(this->ExtendBusWidth(max_bw)); + + /* Get the extended csd. */ + R_TRY(this->IssueCommandSendExtCsd(wb, wb_size)); + AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(wb), alignof(u32))); + this->mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast(wb))); + + /* If the mmc is manufactured by toshiba, try to enable bkops auto. */ + if (is_toshiba && !IsBkopAutoEnable(static_cast(wb))) { + /* NOTE: Nintendo does not check the result of this. */ + this->EnableBkopsAuto(); + } + + /* Extend the bus speed to as fast as we can. */ + const u8 device_type = GetDeviceType(static_cast(wb)); + R_TRY(this->ExtendBusSpeed(device_type, max_sm)); + + /* Enable power saving. */ + hc->SetPowerSaving(true); + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::OnActivate() { + /* Define the possible startup parameters. */ + constexpr const struct { + BusWidth bus_width; + SpeedMode speed_mode; + } StartupParameters[] = { + #if defined(AMS_SDMMC_ENABLE_MMC_HS400) + { BusWidth_8Bit, SpeedMode_MmcHs400 }, + #else + { BusWidth_8Bit, SpeedMode_MmcHighSpeed }, + #endif + { BusWidth_8Bit, SpeedMode_MmcHighSpeed }, + { BusWidth_1Bit, SpeedMode_MmcHighSpeed }, + }; + + /* Try to start up with each set of parameters. */ + Result result; + for (int i = 0; i < static_cast(util::size(StartupParameters)); ++i) { + /* Alias the parameters. */ + const auto ¶ms = StartupParameters[i]; + + /* Set our max bus width/speed mode. */ + this->max_bus_width = params.bus_width; + this->max_speed_mode = params.speed_mode; + + /* Try to start up the device. */ + result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); + if (R_SUCCEEDED(result)) { + /* If we previously failed to start up the device, log the error correction. */ + if (i != 0) { + BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode); + BaseDeviceAccessor::IncrementNumActivationErrorCorrections(); + } + + return ResultSuccess(); + } + + /* Log that our startup failed. */ + BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); + + /* Shut down the host controller before we try to start up again. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + } + + /* We failed to start up with all sets of parameters. */ + BaseDeviceAccessor::PushErrorTimeStamp(); + + return result; + } + + Result MmcDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { + /* Get the sector index alignment. */ + u32 sector_index_alignment = 0; + if (!is_read) { + constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize; + sector_index_alignment = MmcWriteSectorAlignment; + AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment)); + } + + /* Do the read/write. */ + return BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, sector_index_alignment, buf, buf_size, is_read); + } + + Result MmcDeviceAccessor::ReStartup() { + /* Shut down the host controller. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + + /* Perform start up. */ + Result result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); + if (R_FAILED(result)) { + BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); + return result; + } + + return ResultSuccess(); + } + + void MmcDeviceAccessor::Initialize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* If we've already initialized, we don't need to do anything. */ + if (this->is_initialized) { + return; + } + + /* Set the base device to our mmc device. */ + BaseDeviceAccessor::SetDevice(std::addressof(this->mmc_device)); + + /* Initialize. */ + BaseDeviceAccessor::GetHostController()->Initialize(); + this->is_initialized = true; + } + + void MmcDeviceAccessor::Finalize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* If we've already finalized, we don't need to do anything. */ + if (!this->is_initialized) { + return; + } + this->is_initialized = false; + + /* Deactivate the device. */ + BaseDeviceAccessor::Deactivate(); + + /* Finalize the host controller. */ + BaseDeviceAccessor::GetHostController()->Finalize(); + } + + Result MmcDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { + /* Check that we can write to output. */ + AMS_ABORT_UNLESS(out_speed_mode != nullptr); + + /* Get the current speed mode from the ext csd. */ + R_TRY(GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size)); + R_TRY(GetCurrentSpeedModeFromExtCsd(out_speed_mode, static_cast(this->work_buffer))); + + return ResultSuccess(); + } + + void MmcDeviceAccessor::PutMmcToSleep() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* If the device isn't awake, we don't need to do anything. */ + if (!this->mmc_device.IsAwake()) { + return; + } + + /* Put the device to sleep. */ + this->mmc_device.PutToSleep(); + + /* If necessary, put the host controller to sleep. */ + if (this->mmc_device.IsActive()) { + BaseDeviceAccessor::GetHostController()->PutToSleep(); + } + } + + void MmcDeviceAccessor::AwakenMmc() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* If the device is awake, we don't need to do anything. */ + if (this->mmc_device.IsAwake()) { + return; + } + + /* Wake the host controller, if we need to.*/ + if (this->mmc_device.IsActive()) { + const Result result = BaseDeviceAccessor::GetHostController()->Awaken(); + if (R_FAILED(result)) { + BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue()); + } + } + + /* Wake the device. */ + this->mmc_device.Awaken(); + } + + Result MmcDeviceAccessor::SelectMmcPartition(MmcPartition part) { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* Check that we can access the device. */ + R_TRY(this->mmc_device.CheckAccessible()); + + /* Determine the appropriate SWITCH subcommand. */ + CommandSwitch cs; + switch (part) { + case MmcPartition_UserData: cs = CommandSwitch_WritePartitionAccessDefault; break; + case MmcPartition_BootPartition1: cs = CommandSwitch_WritePartitionAccessRwBootPartition1; break; + case MmcPartition_BootPartition2: cs = CommandSwitch_WritePartitionAccessRwBootPartition2; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Change partition. */ + this->current_partition = MmcPartition_Unknown; + { + R_TRY(this->IssueCommandSwitch(cs)); + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + } + this->current_partition = part; + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::EraseMmc() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* Check that we can access the device. */ + R_TRY(this->mmc_device.CheckAccessible()); + + /* Get the partition capacity. */ + u32 part_capacity; + switch (this->current_partition) { + case MmcPartition_UserData: + part_capacity = this->mmc_device.GetMemoryCapacity(); + break; + case MmcPartition_BootPartition1: + case MmcPartition_BootPartition2: + R_TRY(this->GetMmcBootPartitionCapacity(std::addressof(part_capacity))); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Begin the erase. */ + R_TRY(this->IssueCommandEraseGroupStart(0)); + R_TRY(this->IssueCommandEraseGroupEnd(part_capacity - 1)); + + /* Issue the erase command, allowing 30 seconds for it to complete. */ + ManualTimer timer(30000); + Result result = this->IssueCommandErase(); + R_TRY_CATCH(result) { + R_CATCH(sdmmc::ResultDataTimeoutError) { /* Data timeout error is acceptable. */ } + R_CATCH(sdmmc::ResultCommandCompleteSoftwareTimeout) { /* Command complete software timeout error is acceptable. */ } + R_CATCH(sdmmc::ResultBusySoftwareTimeout) { /* Busy software timeout error is acceptable. */ } + } R_END_TRY_CATCH; + + /* Wait for the erase to finish. */ + while (true) { + /* Check if we're done. */ + result = BaseDeviceAccessor::IssueCommandSendStatus(); + if (R_SUCCEEDED(result)) { + break; + } + + /* Otherwise, check if we should reject the error. */ + if (!sdmmc::ResultUnexpectedDeviceState::Includes(result)) { + return result; + } + + /* Check if timeout has been exceeded. */ + R_UNLESS(timer.Update(), sdmmc::ResultMmcEraseSoftwareTimeout()); + } + + /* If the partition is user data, check if we need to perform toshiba-specific erase. */ + if (this->current_partition == MmcPartition_UserData) { + u8 cid[DeviceCidSize]; + this->mmc_device.GetCid(cid, sizeof(cid)); + if (IsToshibaMmc(cid)) { + /* NOTE: Nintendo does not check the result of this operation. */ + this->CancelToshibaMmcModel(); + } + } + + return ResultSuccess(); + } + + Result MmcDeviceAccessor::GetMmcBootPartitionCapacity(u32 *out_num_sectors) const { + /* Get the capacity from the extended csd. */ + AMS_ABORT_UNLESS(out_num_sectors != nullptr); + R_TRY(this->GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size)); + + *out_num_sectors = GetBootPartitionMemoryCapacityFromExtCsd(static_cast(this->work_buffer)); + return ResultSuccess(); + } + + Result MmcDeviceAccessor::GetMmcExtendedCsd(void *dst, size_t dst_size) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); + + /* Check that we can access the device. */ + R_TRY(this->mmc_device.CheckAccessible()); + + /* Get the csd. */ + u8 csd[DeviceCsdSize]; + this->mmc_device.GetCsd(csd, sizeof(csd)); + + /* Check that the card supports ext csd. */ + R_UNLESS(!IsLessThanSpecification4(csd), sdmmc::ResultMmcNotSupportExtendedCsd()); + + /* Get the ext csd. */ + R_TRY(this->IssueCommandSendExtCsd(dst, dst_size)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.hpp new file mode 100644 index 0000000..97ac864 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.hpp @@ -0,0 +1,143 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_base_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + class MmcDevice : public BaseDevice { + private: + static constexpr u16 Rca = 2; + public: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual os::EventType *GetRemovedEvent() const override { + /* Mmc can't be removed. */ + return nullptr; + } + #endif + + virtual DeviceType GetDeviceType() const override { + return DeviceType_Mmc; + } + + virtual u16 GetRca() const override { + return Rca; + } + + void SetOcrAndHighCapacity(u32 ocr); + }; + + class MmcDeviceAccessor : public BaseDeviceAccessor { + private: + MmcDevice mmc_device; + void *work_buffer; + size_t work_buffer_size; + BusWidth max_bus_width; + SpeedMode max_speed_mode; + MmcPartition current_partition; + bool is_initialized; + private: + enum CommandSwitch { + CommandSwitch_SetBitsProductionStateAwarenessEnable = 0, + CommandSwitch_ClearBitsAutoModeEnable = 1, + CommandSwitch_WriteProductionStateAwarenessNormal = 2, + CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites = 3, + CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites = 4, + CommandSwitch_SetBitsBkopsEnAutoEn = 5, + CommandSwitch_WriteBusWidth1Bit = 6, + CommandSwitch_WriteBusWidth4Bit = 7, + CommandSwitch_WriteBusWidth8Bit = 8, + CommandSwitch_WriteBusWidth8BitDdr = 9, + CommandSwitch_WriteHsTimingLegacySpeed = 10, + CommandSwitch_WriteHsTimingHighSpeed = 11, + CommandSwitch_WriteHsTimingHs200 = 12, + CommandSwitch_WriteHsTimingHs400 = 13, + CommandSwitch_WritePartitionAccessDefault = 14, + CommandSwitch_WritePartitionAccessRwBootPartition1 = 15, + CommandSwitch_WritePartitionAccessRwBootPartition2 = 16, + }; + + static constexpr ALWAYS_INLINE u32 GetCommandSwitchArgument(CommandSwitch cs) { + switch (cs) { + case CommandSwitch_SetBitsProductionStateAwarenessEnable: return 0x01111000; + case CommandSwitch_ClearBitsAutoModeEnable: return 0x02112000; + case CommandSwitch_WriteProductionStateAwarenessNormal: return 0x03850000; + case CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites: return 0x03850100; + case CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites: return 0x03850200; + case CommandSwitch_SetBitsBkopsEnAutoEn: return 0x01A30200; + case CommandSwitch_WriteBusWidth1Bit: return 0x03B70000; + case CommandSwitch_WriteBusWidth4Bit: return 0x03B70100; + case CommandSwitch_WriteBusWidth8Bit: return 0x03B70200; + case CommandSwitch_WriteBusWidth8BitDdr: return 0x03B70600; + case CommandSwitch_WriteHsTimingLegacySpeed: return 0x03B90000; + case CommandSwitch_WriteHsTimingHighSpeed: return 0x03B90100; + case CommandSwitch_WriteHsTimingHs200: return 0x03B90200; + case CommandSwitch_WriteHsTimingHs400: return 0x03B90300; + case CommandSwitch_WritePartitionAccessDefault: return 0x03B30000; + case CommandSwitch_WritePartitionAccessRwBootPartition1: return 0x03B30100; + case CommandSwitch_WritePartitionAccessRwBootPartition2: return 0x03B30200; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + private: + Result IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const; + Result IssueCommandSetRelativeAddr() const; + Result IssueCommandSwitch(CommandSwitch cs) const; + Result IssueCommandSendExtCsd(void *dst, size_t dst_size) const; + Result IssueCommandEraseGroupStart(u32 sector_index) const; + Result IssueCommandEraseGroupEnd(u32 sector_index) const; + Result IssueCommandErase() const; + Result CancelToshibaMmcModel(); + Result ChangeToReadyState(BusPower bus_power); + Result ExtendBusWidth(BusWidth max_bus_width); + Result EnableBkopsAuto(); + Result ChangeToHighSpeed(bool check_before); + Result ChangeToHs200(); + Result ChangeToHs400(); + Result ExtendBusSpeed(u8 device_type, SpeedMode max_sm); + Result StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size); + protected: + virtual Result OnActivate() override; + virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; + virtual Result ReStartup() override; + public: + virtual void Initialize() override; + virtual void Finalize() override; + virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; + public: + explicit MmcDeviceAccessor(IHostController *hc) + : BaseDeviceAccessor(hc), work_buffer(nullptr), work_buffer_size(0), + max_bus_width(BusWidth_8Bit), max_speed_mode(SpeedMode_MmcHs400), current_partition(MmcPartition_Unknown), + is_initialized(false) + { + /* ... */ + } + + void SetMmcWorkBuffer(void *wb, size_t wb_size) { + this->work_buffer = wb; + this->work_buffer_size = wb_size; + } + + void PutMmcToSleep(); + void AwakenMmc(); + Result SelectMmcPartition(MmcPartition part); + Result EraseMmc(); + Result GetMmcBootPartitionCapacity(u32 *out_num_sectors) const; + Result GetMmcExtendedCsd(void *dst, size_t dst_size) const; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.cpp new file mode 100644 index 0000000..763869d --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.cpp @@ -0,0 +1,50 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_gc_asic0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller; + GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller)); + + } + + IHostController *GetHostControllerOfPortGcAsic0() { + return std::addressof(g_gc_asic0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() { + return std::addressof(g_gc_asic0_device_accessor); + } + + GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() { + return std::addressof(g_gc_asic0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.hpp new file mode 100644 index 0000000..8dc7880 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.hpp @@ -0,0 +1,28 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_gc_asic_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortGcAsic0(); + IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0(); + GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0(); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.cpp new file mode 100644 index 0000000..30cfc86 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.cpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_mmc0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" +#include "sdmmc_base_device_accessor.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortMmc0 g_mmc0_host_controller; + MmcDeviceAccessor g_mmc0_device_accessor(std::addressof(g_mmc0_host_controller)); + + } + + IHostController *GetHostControllerOfPortMmc0() { + return std::addressof(g_mmc0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortMmc0() { + return std::addressof(g_mmc0_device_accessor); + } + + MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0() { + return std::addressof(g_mmc0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.hpp new file mode 100644 index 0000000..b675bd4 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.hpp @@ -0,0 +1,28 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_mmc_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortMmc0(); + IDeviceAccessor *GetDeviceAccessorOfPortMmc0(); + MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0(); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.cpp new file mode 100644 index 0000000..5288a5e --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.cpp @@ -0,0 +1,63 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_sd_card0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortSdCard0 g_sd_card0_host_controller; + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + + constexpr inline u32 SdCard0DebounceMilliSeconds = 128; + DeviceDetector g_sd_card0_detector(gpio::DeviceCode_SdCd, gpio::GpioValue_Low, SdCard0DebounceMilliSeconds); + + SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller), std::addressof(g_sd_card0_detector)); + + #else + + SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller)); + + #endif + + + } + + IHostController *GetHostControllerOfPortSdCard0() { + return std::addressof(g_sd_card0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortSdCard0() { + return std::addressof(g_sd_card0_device_accessor); + } + + SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0() { + return std::addressof(g_sd_card0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.hpp new file mode 100644 index 0000000..690dbb6 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.hpp @@ -0,0 +1,28 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_sd_card_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortSdCard0(); + IDeviceAccessor *GetDeviceAccessorOfPortSdCard0(); + SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0(); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.cpp new file mode 100644 index 0000000..9bfff1a --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.cpp @@ -0,0 +1,1054 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sd_card_device_accessor.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() std::scoped_lock lk(this->sd_card_device.device_mutex) + + #else + + #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() + + #endif + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + + #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() R_UNLESS(!this->sd_card_device.IsRemoved(), sdmmc::ResultDeviceRemoved()) + + #else + + #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() + + #endif + + namespace { + + constexpr inline u32 OcrCardPowerUpStatus = (1 << 31); + constexpr inline u32 OcrCardCapacityStatus = (1 << 30); + constexpr inline u32 OcrSwitchingTo1_8VAccepted = (1 << 24); + + constexpr bool IsLessThanSpecification1_1(const u8 *scr) { + AMS_ABORT_UNLESS(scr != nullptr); + + const u8 sd_spec = scr[0] & 0xF; + return sd_spec < 1; + } + + constexpr u32 GetSendOpCmdArgument(bool spec_under_2, bool uhs_i_supported) { + const u32 hcs = !spec_under_2 ? (1u << 30) : (0u << 30); + const u32 xpc = !spec_under_2 ? (1u << 28) : (0u << 28); + const u32 s18r = (!spec_under_2 && uhs_i_supported) ? (1u << 24) : (0u << 24); + return hcs | xpc | s18r | 0x00100000u; + } + + constexpr bool IsLessThanCsdVersion2(const u8 *csd) { + AMS_ABORT_UNLESS(csd != nullptr); + + /* Check whether CSD_STRUCTURE is 0. */ + return ((csd[14] & 0xC0) >> 6) == 0; + } + + constexpr u32 GetMemoryCapacityFromCsd(const u16 *csd) { + AMS_ABORT_UNLESS(csd != nullptr); + + /* Get CSIZE, convert appropriately. */ + const u32 csize = (static_cast(csd[3] & 0x3FFF) << 8) | (static_cast(csd[2] & 0xFF00) >> 8); + return (1 + csize) << 10; + } + + constexpr u8 GetSdBusWidths(const u8 *scr) { + AMS_ABORT_UNLESS(scr != nullptr); + return scr[1] & 0xF; + } + + constexpr bool IsSupportedBusWidth4Bit(u8 sd_bw) { + return (sd_bw & 0x4) != 0; + } + + constexpr bool IsSupportedAccessMode(const u8 *status, SwitchFunctionAccessMode access_mode) { + AMS_ABORT_UNLESS(status != nullptr); + + return (status[13] & (1u << access_mode)) != 0; + } + + constexpr u8 GetAccessModeFromFunctionSelection(const u8 *status) { + AMS_ABORT_UNLESS(status != nullptr); + return (status[16] & 0xF); + } + + constexpr bool IsAccessModeInFunctionSelection(const u8 *status, SwitchFunctionAccessMode mode) { + return GetAccessModeFromFunctionSelection(status) == static_cast(mode); + } + + constexpr u16 GetMaximumCurrentConsumption(const u8 *status) { + AMS_ABORT_UNLESS(status != nullptr); + + return (static_cast(status[0]) << 8) | + (static_cast(status[1]) << 0); + } + + constexpr u32 GetSizeOfProtectedArea(const u8 *sd_status) { + return (static_cast(sd_status[4]) << 24) | + (static_cast(sd_status[5]) << 16) | + (static_cast(sd_status[6]) << 8) | + (static_cast(sd_status[7]) << 0); + } + + Result GetCurrentSpeedMode(SpeedMode *out_sm, const u8 *status, bool is_uhs_i) { + AMS_ABORT_UNLESS(out_sm != nullptr); + + /* Get the access mode. */ + switch (static_cast(GetAccessModeFromFunctionSelection(status))) { + case SwitchFunctionAccessMode_Default: + if (is_uhs_i) { + *out_sm = SpeedMode_SdCardSdr12; + } else { + *out_sm = SpeedMode_SdCardDefaultSpeed; + } + break; + case SwitchFunctionAccessMode_HighSpeed: + if (is_uhs_i) { + *out_sm = SpeedMode_SdCardSdr25; + } else { + *out_sm = SpeedMode_SdCardHighSpeed; + } + break; + case SwitchFunctionAccessMode_Sdr50: + *out_sm = SpeedMode_SdCardSdr50; + break; + case SwitchFunctionAccessMode_Sdr104: + *out_sm = SpeedMode_SdCardSdr104; + break; + case SwitchFunctionAccessMode_Ddr50: + *out_sm = SpeedMode_SdCardDdr50; + break; + default: + return sdmmc::ResultUnexpectedSdCardSwitchFunctionStatus(); + } + + return ResultSuccess(); + } + + } + + void SdCardDevice::SetOcrAndHighCapacity(u32 ocr) { + /* Set ocr. */ + BaseDevice::SetOcr(ocr); + + /* Set high capacity. */ + BaseDevice::SetHighCapacity((ocr & OcrCardCapacityStatus) != 0); + } + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + void SdCardDeviceAccessor::RemovedCallback() { + /* Signal that the device was removed. */ + this->sd_card_device.SignalRemovedEvent(); + + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Shut down. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + } + #endif + + Result SdCardDeviceAccessor::IssueCommandSendRelativeAddr(u16 *out_rca) const { + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R6; + Command command(CommandIndex_SendRelativeAddr, 0, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Set the output rca. */ + AMS_ABORT_UNLESS(out_rca != nullptr); + *out_rca = static_cast(resp >> 16); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendIfCond() const { + /* Get the argument. */ + constexpr u32 SendIfCommandArgument = 0x01AAu; + constexpr u32 SendIfCommandArgumentMask = 0x0FFFu; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R7; + Command command(CommandIndex_SendIfCond, SendIfCommandArgument, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Verify that our argument was returned to us. */ + R_UNLESS((resp & SendIfCommandArgumentMask) == (SendIfCommandArgument & SendIfCommandArgumentMask), sdmmc::ResultSdCardValidationError()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); + + /* Get the argument. */ + constexpr u32 CheckSupportedFunctionArgument = 0x00FFFFFF; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_Switch, CheckSupportedFunctionArgument, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 1, TransferDirection_ReadFromDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); + + /* Get the argument. */ + const u32 arg = (set_function ? (1u << 31) : (0u << 31)) | 0x00FFFFF0 | static_cast(access_mode); + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_Switch, arg, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 1, TransferDirection_ReadFromDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandVoltageSwitch() const { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_VoltageSwitch, 0, false, DeviceState_Ready)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask) const { + /* Get arg. */ + const u32 arg = static_cast(this->sd_card_device.GetRca()) << 16; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_AppCmd, arg, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Mask out the ignored status bits. */ + if (ignore_mask != 0) { + resp &= ~ignore_mask; + } + + /* Check the device status. */ + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + /* Check the app command bit. */ + R_UNLESS((resp & DeviceStatus_AppCmd) != 0, sdmmc::ResultUnexpectedSdCardAcmdDisabled()); + + /* Check the device state. */ + if (expected_state != DeviceState_Unknown) { + R_UNLESS(this->sd_card_device.GetDeviceState(resp) == expected_state, sdmmc::ResultUnexpectedDeviceState()); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSetBusWidth4Bit() const { + /* Issue the application command. */ + constexpr u32 Arg = 0x2; + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetBusWidth, Arg, false, DeviceState_Tran)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSdStatus(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSdStatusSize); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(SdApplicationCommandIndex_SdStatus, 0, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSdStatusSize, 1, TransferDirection_ReadFromDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const { + /* Get the argument. */ + const u32 arg = GetSendOpCmdArgument(spec_under_2, uhs_i_supported); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R3; + Command command(SdApplicationCommandIndex_SdSendOpCond, arg, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + hc->GetLastResponse(out_ocr, sizeof(u32), CommandResponseType); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandClearCardDetect() const { + /* Issue the application command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetClearCardDetect, 0, false, DeviceState_Tran)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendScr(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardScrSize); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(SdApplicationCommandIndex_SendScr, 0, CommandResponseType, false); + TransferData xfer_data(dst, SdCardScrSize, 1, TransferDirection_ReadFromDevice); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::EnterUhsIMode() { + /* Send voltage switch command. */ + R_TRY(this->IssueCommandVoltageSwitch()); + + /* Switch to sdr12. */ + R_TRY(BaseDeviceAccessor::GetHostController()->SwitchToSdr12()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ChangeToReadyState(bool spec_under_2, bool uhs_i_supported) { + /* Decide on an ignore mask. */ + u32 ignore_mask = spec_under_2 ? static_cast(DeviceStatus_IllegalCommand) : 0u; + + /* Be prepared to wait up to 3.0 seconds to change state. */ + ManualTimer timer(3000); + while (true) { + /* We want to get ocr, which requires our sending an application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Unknown, ignore_mask)); + ignore_mask = 0; + + /* Get the ocr, and check if we're done. */ + u32 ocr; + R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), spec_under_2, uhs_i_supported)); + + if ((ocr & OcrCardPowerUpStatus) != 0) { + this->sd_card_device.SetOcrAndHighCapacity(ocr); + + /* Handle uhs i mode. */ + this->sd_card_device.SetUhsIMode(false); + if (uhs_i_supported && ((ocr & OcrSwitchingTo1_8VAccepted) != 0)) { + R_TRY(this->EnterUhsIMode()); + this->sd_card_device.SetUhsIMode(true); + } + + return ResultSuccess(); + } + + /* Check if we've timed out. */ + R_UNLESS(timer.Update(), sdmmc::ResultSdCardInitializationSoftwareTimeout()); + + /* Try again in 1ms. */ + WaitMicroSeconds(1000); + } + } + + Result SdCardDeviceAccessor::ChangeToStbyStateAndGetRca() { + /* Be prepared to wait up to 1.0 seconds to change state. */ + ManualTimer timer(1000); + while (true) { + /* Get rca. */ + u16 rca; + R_TRY(this->IssueCommandSendRelativeAddr(std::addressof(rca))); + if (rca != 0) { + this->sd_card_device.SetRca(rca); + return ResultSuccess(); + } + + /* Check if we've timed out. */ + R_UNLESS(timer.Update(), sdmmc::ResultSdCardGetValidRcaSoftwareTimeout()); + } + } + + Result SdCardDeviceAccessor::SetMemoryCapacity(const void *csd) { + if (IsLessThanCsdVersion2(static_cast(csd))) { + R_TRY(this->sd_card_device.SetLegacyMemoryCapacity()); + } else { + AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(csd), alignof(u16))); + this->sd_card_device.SetMemoryCapacity(GetMemoryCapacityFromCsd(static_cast(csd))); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetScr(void *dst, size_t dst_size) const { + /* Issue the application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSendScr(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusWidth(BusWidth max_bw, u8 sd_bw) { + /* If the maximum bus width is 1bit, we can't extend. */ + R_SUCCEED_IF(max_bw == BusWidth_1Bit); + + /* If 4bit mode isn't supported, we can't extend. */ + R_SUCCEED_IF(!IsSupportedBusWidth4Bit(sd_bw)); + + /* If the host controller doesn't support 4bit mode, we can't extend. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_SUCCEED_IF(!hc->IsSupportedBusWidth(BusWidth_4Bit)); + + /* Issue the application command to change to 4bit mode. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSetBusWidth4Bit()); + + /* Set the host controller's bus width. */ + hc->SetBusWidth(BusWidth_4Bit); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size) { + /* Issue command to check if we can switch access mode. */ + R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, false, access_mode)); + R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardCannotSwitchAccessMode()); + + /* Check if we can accept the resulting current consumption. */ + constexpr u16 AcceptableCurrentLimit = 800; /* mA */ + R_UNLESS(GetMaximumCurrentConsumption(static_cast(wb)) < AcceptableCurrentLimit, sdmmc::ResultSdCardUnacceptableCurrentConsumption()); + + /* Switch the access mode. */ + R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, true, access_mode)); + R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardFailedSwitchAccessMode()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size) { + /* Check that we're in 4bit bus mode. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_UNLESS(hc->GetBusWidth() == BusWidth_4Bit, sdmmc::ResultSdCardNot4BitBusWidthAtUhsIMode()); + + /* Determine what speed mode/access mode we should switch to. */ + R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); + SwitchFunctionAccessMode target_am; + SpeedMode target_sm; + if (max_sm == SpeedMode_SdCardSdr104 && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr104)) { + target_am = SwitchFunctionAccessMode_Sdr104; + target_sm = SpeedMode_SdCardSdr104; + } else if ((max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50) && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr50)) { + target_am = SwitchFunctionAccessMode_Sdr50; + target_sm = SpeedMode_SdCardSdr50; + } else { + return sdmmc::ResultSdCardNotSupportSdr104AndSdr50(); + } + + /* Switch the access mode. */ + R_TRY(this->SwitchAccessMode(target_am, wb, wb_size)); + + /* Set the host controller speed mode and perform tuning using command index 19. */ + R_TRY(hc->SetSpeedMode(target_sm)); + R_TRY(hc->Tuning(target_sm, 19)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size) { + /* If the maximum speed is default speed, we have nothing to do. */ + R_SUCCEED_IF(max_sm == SpeedMode_SdCardDefaultSpeed); + + /* Otherwise, if the spec is under 1.1, we have nothing to do. */ + R_SUCCEED_IF(spec_under_1_1); + + /* Otherwise, Check if high speed is supported. */ + R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); + R_SUCCEED_IF(!IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_HighSpeed)); + + /* Switch the access mode. */ + R_TRY(this->SwitchAccessMode(SwitchFunctionAccessMode_HighSpeed, wb, wb_size)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + /* Set the host controller speed mode. */ + R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_SdCardHighSpeed)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdStatus(void *dst, size_t dst_size) const { + /* Issue the application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSdStatus(dst, dst_size)); + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::TryDisconnectDat3PullUpResistor() const { + /* Issue the application command to clear card detect. */ + /* NOTE: Nintendo accepts a failure. */ + if (R_SUCCEEDED(this->IssueCommandAppCmd(DeviceState_Tran))) { + /* NOTE: Nintendo does not check the result of this. */ + this->IssueCommandClearCardDetect(); + } + + /* NOTE: Nintendo does not check the result of this. */ + BaseDeviceAccessor::IssueCommandSendStatus(); + } + + Result SdCardDeviceAccessor::StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) { + /* Start up the host controller. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->Startup(BusPower_3_3V, BusWidth_1Bit, SpeedMode_SdCardIdentification, false)); + + /* Wait 1ms for configuration to take. */ + WaitMicroSeconds(1000); + + /* Wait an additional 74 clocks for configuration to take. */ + WaitClocks(74, hc->GetDeviceClockFrequencyKHz()); + + /* Go to idle state. */ + R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState()); + + /* Check whether the spec is under 2.0. */ + bool spec_under_2 = false; + R_TRY_CATCH(this->IssueCommandSendIfCond()) { + R_CATCH(sdmmc::ResultResponseTimeoutError) { spec_under_2 = true; } + } R_END_TRY_CATCH; + + /* Set the rca to 0. */ + this->sd_card_device.SetRca(0); + + /* Go to ready state. */ + const bool can_use_uhs_i_mode = (max_bw != BusWidth_1Bit) && (max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50); + const bool uhs_i_supported = hc->IsSupportedTuning() && hc->IsSupportedBusPower(BusPower_1_8V); + R_TRY(this->ChangeToReadyState(spec_under_2, can_use_uhs_i_mode && uhs_i_supported)); + + /* Get the CID. */ + R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size)); + this->sd_card_device.SetCid(wb, wb_size); + + /* Go to stby state and get the RCA. */ + R_TRY(this->ChangeToStbyStateAndGetRca()); + + /* Get the CSD. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size)); + this->sd_card_device.SetCsd(wb, wb_size); + R_TRY(this->SetMemoryCapacity(wb)); + + /* Set the host controller speed mode to default if we're not in uhs i mode. */ + if (!this->sd_card_device.IsUhsIMode()) { + R_TRY(hc->SetSpeedMode(SpeedMode_SdCardDefaultSpeed)); + } + + /* Issue select card command. */ + R_TRY(BaseDeviceAccessor::IssueCommandSelectCard()); + + /* Set block length to sector size. */ + R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize()); + + /* Try to disconnect dat3 pullup resistor. */ + TryDisconnectDat3PullUpResistor(); + + /* Get the SCR. */ + R_TRY(this->GetScr(wb, wb_size)); + const u8 sd_bw = GetSdBusWidths(static_cast(wb)); + const bool spec_under_1_1 = IsLessThanSpecification1_1(static_cast(wb)); + + /* Extend the bus width to the largest that we can. */ + R_TRY(this->ExtendBusWidth(max_bw, sd_bw)); + + /* Extend the bus speed to as fast as we can. */ + if (this->sd_card_device.IsUhsIMode()) { + R_TRY(this->ExtendBusSpeedAtUhsIMode(max_sm, wb, wb_size)); + } else { + R_TRY(this->ExtendBusSpeedAtNonUhsIMode(max_sm, spec_under_1_1, wb, wb_size)); + } + + /* Enable power saving. */ + hc->SetPowerSaving(true); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::OnActivate() { + /* Define the possible startup parameters. */ + constexpr const struct { + BusWidth bus_width; + SpeedMode speed_mode; + } StartupParameters[] = { + #if defined(AMS_SDMMC_ENABLE_SD_UHS_I) + { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, + { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, + { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, + #else + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, + { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, + #endif + }; + + /* Try to start up with each set of parameters. */ + Result result; + for (int i = 0; i < static_cast(util::size(StartupParameters)); ++i) { + /* Alias the parameters. */ + const auto ¶ms = StartupParameters[i]; + + /* Set our max bus width/speed mode. */ + this->max_bus_width = params.bus_width; + this->max_speed_mode = params.speed_mode; + + /* Try to start up the device. */ + result = this->StartupSdCardDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); + if (R_SUCCEEDED(result)) { + /* If we previously failed to start up the device, log the error correction. */ + if (i != 0) { + BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode); + BaseDeviceAccessor::IncrementNumActivationErrorCorrections(); + } + + return ResultSuccess(); + } + + /* Check if we were removed. */ + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + + /* Log that our startup failed. */ + BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); + + /* Shut down the host controller before we try to start up again. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + } + + /* We failed to start up with all sets of parameters. */ + /* Check the csd for errors. */ + if (sdmmc::ResultUnexpectedDeviceCsdValue::Includes(result)) { + u32 csd[DeviceCsdSize / sizeof(u32)]; + this->sd_card_device.GetCsd(csd, sizeof(csd)); + BaseDeviceAccessor::PushErrorLog(false, "%06X%08X%08X%08X", csd[3] & 0x00FFFFFF, csd[2], csd[1], csd[0]); + } + BaseDeviceAccessor::PushErrorTimeStamp(); + + /* Check if we failed because the sd card is removed. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { + WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + } + #endif + + return result; + } + + Result SdCardDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { + /* Do the read/write. */ + const Result result = BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, 0, buf, buf_size, is_read); + + /* Check if we failed because the sd card is removed. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { + WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + } + #endif + + return result; + } + + Result SdCardDeviceAccessor::ReStartup() { + /* Shut down the host controller. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + + /* Perform start up. */ + Result result = this->StartupSdCardDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); + if (R_FAILED(result)) { + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + + BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); + return result; + } + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::Initialize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If we've already initialized, we don't need to do anything. */ + if (this->is_initialized) { + return; + } + + /* Set the base device to our sd card device. */ + BaseDeviceAccessor::SetDevice(std::addressof(this->sd_card_device)); + + /* Initialize. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + /* TODO: We probably want this (and other sd card detection stuff) to be conditional pcv control active. */ + /* This will be a requirement to support sd card access with detector in stratosphere before PCV is alive. */ + this->sd_card_device.InitializeRemovedEvent(); + hc->PreSetRemovedEvent(this->sd_card_device.GetRemovedEvent()); + CallbackInfo ci = { + .inserted_callback = nullptr, + .inserted_callback_arg = this, + .removed_callback = RemovedCallbackEntry, + .removed_callback_arg = this, + }; + this->sd_card_detector->Initialize(std::addressof(ci)); + } + #endif + hc->Initialize(); + + /* Mark ourselves as initialized. */ + this->is_initialized = true; + } + + void SdCardDeviceAccessor::Finalize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If we've already finalized, we don't need to do anything. */ + if (!this->is_initialized) { + return; + } + this->is_initialized = false; + + /* Deactivate the device. */ + BaseDeviceAccessor::Deactivate(); + + /* Finalize the host controller. */ + BaseDeviceAccessor::GetHostController()->Finalize(); + + /* Finalize the detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + this->sd_card_detector->Finalize(); + this->sd_card_device.FinalizeRemovedEvent(); + } + #endif + } + + Result SdCardDeviceAccessor::Activate() { + /* Activate the detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're awake. */ + R_UNLESS(this->sd_card_device.IsAwake(), sdmmc::ResultNotAwakened()); + + /* Check that we're not already active. */ + R_SUCCEED_IF(this->sd_card_device.IsActive()); + + /* Clear the removed event. */ + this->sd_card_device.ClearRemovedEvent(); + + /* Check that the SD card is inserted. */ + R_UNLESS(this->sd_card_detector->IsInserted(), sdmmc::ResultNoDevice()); + } + #endif + + /* Activate the base device. */ + return BaseDeviceAccessor::Activate(); + } + + Result SdCardDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { + /* Check that we can write to output. */ + AMS_ABORT_UNLESS(out_speed_mode != nullptr); + + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus default speed). */ + R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); + if (IsLessThanSpecification1_1(static_cast(this->work_buffer))) { + *out_speed_mode = SpeedMode_SdCardDefaultSpeed; + return ResultSuccess(); + } + + /* Get the current speed mode. */ + R_TRY(this->IssueCommandCheckSupportedFunction(this->work_buffer, this->work_buffer_size)); + R_TRY(GetCurrentSpeedMode(out_speed_mode, static_cast(this->work_buffer), this->sd_card_device.IsUhsIMode())); + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::PutSdCardToSleep() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If the device isn't awake, we don't need to do anything. */ + if (!this->sd_card_device.IsAwake()) { + return; + } + + /* Put the device to sleep. */ + this->sd_card_device.PutToSleep(); + + /* Put the detector to sleep. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + this->sd_card_detector->PutToSleep(); + #endif + + /* If necessary, put the host controller to sleep. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) + #else + if (this->sd_card_device.IsActive()) + #endif + { + BaseDeviceAccessor::GetHostController()->PutToSleep(); + } + } + + void SdCardDeviceAccessor::AwakenSdCard() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If the device is awake, we don't need to do anything. */ + if (this->sd_card_device.IsAwake()) { + return; + } + + /* Wake the host controller, if we need to.*/ + bool force_det = false; + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) + #else + if (this->sd_card_device.IsActive()) + #endif + { + const Result result = BaseDeviceAccessor::GetHostController()->Awaken(); + if (R_SUCCEEDED(result)) { + force_det = R_FAILED(BaseDeviceAccessor::IssueCommandSendStatus()); + } else { + BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue()); + force_det = true; + } + } + + /* Wake the detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + this->sd_card_detector->Awaken(force_det); + #else + AMS_UNUSED(force_det); + #endif + + /* Wake the device. */ + this->sd_card_device.Awaken(); + } + + Result SdCardDeviceAccessor::GetSdCardScr(void *dst, size_t dst_size) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Get the SCR. */ + R_TRY(this->GetScr(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus can't switch). */ + R_TRY(this->GetScr(dst, dst_size)); + R_UNLESS(!IsLessThanSpecification1_1(static_cast(dst)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); + + /* Get the status. */ + if (switch_function == SdCardSwitchFunction_CheckSupportedFunction) { + R_TRY(this->IssueCommandCheckSupportedFunction(dst, dst_size)); + } else { + SwitchFunctionAccessMode am; + switch (switch_function) { + case SdCardSwitchFunction_CheckDefault: am = SwitchFunctionAccessMode_Default; break; + case SdCardSwitchFunction_CheckHighSpeed: am = SwitchFunctionAccessMode_HighSpeed; break; + case SdCardSwitchFunction_CheckSdr50: am = SwitchFunctionAccessMode_Sdr50; break; + case SdCardSwitchFunction_CheckSdr104: am = SwitchFunctionAccessMode_Sdr104; break; + case SdCardSwitchFunction_CheckDdr50: am = SwitchFunctionAccessMode_Ddr50; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + R_TRY(this->IssueCommandSwitchAccessMode(dst, dst_size, false, am)); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus can't switch). */ + R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); + R_UNLESS(!IsLessThanSpecification1_1(static_cast(this->work_buffer)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); + + /* Determine the access mode. */ + SwitchFunctionAccessMode am; + switch (speed_mode) { + case SpeedMode_SdCardSdr12: + case SpeedMode_SdCardDefaultSpeed: + am = SwitchFunctionAccessMode_Default; + break; + case SpeedMode_SdCardSdr25: + case SpeedMode_SdCardHighSpeed: + am = SwitchFunctionAccessMode_HighSpeed; + break; + case SpeedMode_SdCardSdr50: + am = SwitchFunctionAccessMode_Sdr50; + break; + case SpeedMode_SdCardSdr104: + am = SwitchFunctionAccessMode_Sdr104; + break; + case SpeedMode_SdCardDdr50: + am = SwitchFunctionAccessMode_Ddr50; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check that the mode is supported. */ + R_TRY(this->IssueCommandSwitchAccessMode(this->work_buffer, this->work_buffer_size, false, am)); + R_UNLESS(IsSupportedAccessMode(static_cast(this->work_buffer), am), sdmmc::ResultSdCardNotSupportAccessMode()); + + /* Get the current consumption. */ + AMS_ABORT_UNLESS(out_current_consumption != nullptr); + *out_current_consumption = GetMaximumCurrentConsumption(static_cast(this->work_buffer)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardSdStatus(void *dst, size_t dst_size) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Get the status. */ + R_TRY(this->GetSdStatus(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const { + AMS_ABORT_UNLESS(out_num_sectors != nullptr); + + /* Get the sd status. */ + R_TRY(this->GetSdCardSdStatus(this->work_buffer, this->work_buffer_size)); + const u32 size_of_protected_area = GetSizeOfProtectedArea(static_cast(this->work_buffer)); + + /* Get the csd. */ + u8 csd[DeviceCsdSize]; + this->sd_card_device.GetCsd(csd, sizeof(csd)); + + /* Handle based on csd version. */ + if (IsLessThanCsdVersion2(csd)) { + /* Get c_size_mult and read_bl_len. */ + u8 c_size_mult, read_bl_len; + this->sd_card_device.GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len)); + + /* Validate the parameters. */ + R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue()); + + /* Calculate capacity. */ + *out_num_sectors = size_of_protected_area << ((read_bl_len + c_size_mult + 2) - 9); + } else { + /* SIZE_OF_PROTECTED_AREA is in bytes. */ + *out_num_sectors = size_of_protected_area / SectorSize; + } + + return ResultSuccess(); + } + + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.hpp new file mode 100644 index 0000000..4f37b0c --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_card_device_accessor.hpp @@ -0,0 +1,222 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_base_device_accessor.hpp" + +#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) +#include "sdmmc_device_detector.hpp" +#endif + +namespace ams::sdmmc::impl { + + class SdCardDevice : public BaseDevice { + private: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + mutable os::EventType removed_event; + #endif + u16 rca; + bool is_valid_rca; + bool is_uhs_i_mode; + public: + SdCardDevice() : rca(0) { + this->OnDeactivate(); + } + + virtual void Deactivate() override { + this->OnDeactivate(); + BaseDevice::Deactivate(); + } + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + virtual os::EventType *GetRemovedEvent() const override { + return std::addressof(this->removed_event); + } + #elif defined(AMS_SDMMC_USE_OS_EVENTS) + virtual os::EventType *GetRemovedEvent() const override { + /* Mmc can't be removed. */ + return nullptr; + } + #endif + + virtual DeviceType GetDeviceType() const override { + return DeviceType_SdCard; + } + + virtual u16 GetRca() const override { + AMS_ABORT_UNLESS(this->is_valid_rca); + return this->rca; + } + + void OnDeactivate() { + this->is_valid_rca = false; + this->is_uhs_i_mode = false; + } + + void SetRca(u16 v) { + this->rca = v; + this->is_valid_rca = true; + } + + void SetOcrAndHighCapacity(u32 ocr); + + void SetUhsIMode(bool en) { + this->is_uhs_i_mode = en; + } + + bool IsUhsIMode() const { + return this->is_uhs_i_mode; + } + }; + + enum SdCardApplicationCommandIndex : std::underlying_type::type { + SdApplicationCommandIndex_SetBusWidth = 6, + + SdApplicationCommandIndex_SdStatus = 13, + + SdApplicationCommandIndex_SendNumWriteBlocks = 22, + SdApplicationCommandIndex_SetWriteBlockEraseCount = 23, + + SdApplicationCommandIndex_SdSendOpCond = 41, + SdApplicationCommandIndex_SetClearCardDetect = 42, + + SdApplicationCommandIndex_SendScr = 51, + }; + + enum SwitchFunctionAccessMode { + SwitchFunctionAccessMode_Default = 0, + SwitchFunctionAccessMode_HighSpeed = 1, + SwitchFunctionAccessMode_Sdr50 = 2, + SwitchFunctionAccessMode_Sdr104 = 3, + SwitchFunctionAccessMode_Ddr50 = 4, + }; + + class SdCardDeviceAccessor : public BaseDeviceAccessor { + private: + SdCardDevice sd_card_device; + void *work_buffer; + size_t work_buffer_size; + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + DeviceDetector *sd_card_detector; + #endif + BusWidth max_bus_width; + SpeedMode max_speed_mode; + bool is_initialized; + private: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + void RemovedCallback(); + + static void RemovedCallbackEntry(void *arg) { + static_cast(arg)->RemovedCallback(); + } + #endif + + Result IssueCommandSendRelativeAddr(u16 *out_rca) const; + Result IssueCommandSendIfCond() const; + Result IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const; + Result IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const; + Result IssueCommandVoltageSwitch() const; + Result IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask = 0) const; + Result IssueCommandSetBusWidth4Bit() const; + Result IssueCommandSdStatus(void *dst, size_t dst_size) const; + Result IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const; + Result IssueCommandClearCardDetect() const; + Result IssueCommandSendScr(void *dst, size_t dst_size) const; + + Result EnterUhsIMode(); + Result ChangeToReadyState(bool spec_under_2, bool uhs_i_supported); + Result ChangeToStbyStateAndGetRca(); + Result SetMemoryCapacity(const void *csd); + Result GetScr(void *dst, size_t dst_size) const; + Result ExtendBusWidth(BusWidth max_bw, u8 sd_bw); + Result SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size); + Result ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size); + Result ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size); + Result GetSdStatus(void *dst, size_t dst_size) const; + Result StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size); + + void TryDisconnectDat3PullUpResistor() const; + protected: + virtual Result OnActivate() override; + virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; + virtual Result ReStartup() override; + public: + virtual void Initialize() override; + virtual void Finalize() override; + virtual Result Activate() override; + virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; + public: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + explicit SdCardDeviceAccessor(IHostController *hc, DeviceDetector *dd) : BaseDeviceAccessor(hc), sd_card_detector(dd) + #else + explicit SdCardDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc) + #endif + { + this->work_buffer = nullptr; + this->work_buffer_size = 0; + this->max_bus_width = BusWidth_4Bit; + this->max_speed_mode = SpeedMode_SdCardSdr104; + this->is_initialized = false; + } + + void SetSdCardWorkBuffer(void *wb, size_t wb_size) { + this->work_buffer = wb; + this->work_buffer_size = wb_size; + } + + void PutSdCardToSleep(); + void AwakenSdCard(); + Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const; + Result GetSdCardScr(void *dst, size_t dst_size) const; + Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const; + Result GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const; + Result GetSdCardSdStatus(void *dst, size_t dst_size) const; + + bool IsSdCardInserted() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->IsInserted(); + #else + AMS_ABORT("IsSdCardInserted without SdCardDetector"); + #endif + } + + bool IsSdCardRemoved() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_device.IsRemoved(); + #else + AMS_ABORT("IsSdCardRemoved without SdCardDetector"); + #endif + } + + void RegisterSdCardDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->RegisterDetectionEventCallback(cb, arg); + #else + AMS_UNUSED(cb, arg); + AMS_ABORT("RegisterSdCardDetectionEventCallback without SdCardDetector"); + #endif + } + + void UnregisterSdCardDetectionEventCallback() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->UnregisterDetectionEventCallback(); + #else + AMS_ABORT("UnregisterSdCardDetectionEventCallback without SdCardDetector"); + #endif + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.cpp new file mode 100644 index 0000000..f03d153 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.cpp @@ -0,0 +1,1039 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sd_host_standard_controller.hpp" +#include "sdmmc_timer.hpp" + +#if defined(ATMOSPHERE_IS_STRATOSPHERE) + #include +#endif + + +namespace ams::sdmmc::impl { + + namespace { + + constexpr inline u32 ControllerReactionTimeoutMilliSeconds = 2000; + constexpr inline u32 CommandTimeoutMilliSeconds = 2000; + constexpr inline u32 DefaultCheckTransferIntervalMilliSeconds = 1500; + constexpr inline u32 BusyTimeoutMilliSeconds = 2000; + + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void SdHostStandardController::ResetBufferInfos() { + for (auto &info : this->buffer_infos) { + info.buffer_address = 0; + info.buffer_size = 0; + } + } + + dd::DeviceVirtualAddress SdHostStandardController::GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size) { + /* Try to find the buffer in our registered regions. */ + dd::DeviceVirtualAddress device_addr = 0; + for (const auto &info : this->buffer_infos) { + if (info.buffer_address <= buffer && (buffer + buffer_size) <= (info.buffer_address + info.buffer_size)) { + device_addr = info.buffer_device_virtual_address + (buffer - info.buffer_address); + break; + } + } + + /* Ensure that we found the buffer. */ + AMS_ABORT_UNLESS(device_addr != 0); + return device_addr; + } + #endif + + void SdHostStandardController::EnsureControl() { + /* Perform a read of clock control to be sure previous configuration takes. */ + reg::Read(this->registers->clock_control); + } + + Result SdHostStandardController::EnableInternalClock() { + /* Enable internal clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, OSCILLATE)); + this->EnsureControl(); + + /* Wait for the internal clock to become stable. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the clock is steady. */ + if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, READY))) { + break; + } + + /* If not, check for timeout. */ + R_UNLESS(timer.Update(), sdmmc::ResultInternalClockStableSoftwareTimeout()); + } + } + + /* Configure to use host controlled divided clock. */ + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, HOST_DRIVER)); + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, DIVIDED_CLOCK)); + + /* Set host version 4.0.0 enable. */ + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, VERSION_4)); + + /* Set host 64 bit addressing enable. */ + AMS_ABORT_UNLESS(reg::HasValue(this->registers->capabilities, SD_REG_BITS_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, SUPPORTED))); + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 64_BIT_ADDRESSING)); + + /* Select SDMA mode. */ + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DMA_SELECT, SDMA)); + + /* Configure timeout control to use the maximum timeout value (TMCLK * 2^27) */ + reg::ReadWrite(this->registers->timeout_control, SD_REG_BITS_VALUE(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0b1110)); + + return ResultSuccess(); + } + + void SdHostStandardController::SetBusPower(BusPower bus_power) { + /* Check that we support the bus power. */ + AMS_ABORT_UNLESS(this->IsSupportedBusPower(bus_power)); + + /* Set the appropriate power. */ + switch (bus_power) { + case BusPower_Off: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, OFF)); + break; + case BusPower_1_8V: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1_8V)); + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); + break; + case BusPower_3_3V: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 3_3V)); + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SdHostStandardController::EnableInterruptStatus() { + /* Set the status register interrupt enables. */ + reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); + reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); + + /* Read/write the interrupt enables to be sure they take. */ + reg::Write(this->registers->normal_int_enable, reg::Read(this->registers->normal_int_enable)); + reg::Write(this->registers->error_int_enable, reg::Read(this->registers->error_int_enable)); + + /* If we're using interrupt events, configure appropriately. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Clear interrupts. */ + this->ClearInterrupt(); + + /* Enable the interrupt signals. */ + reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); + reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); + } + #endif + } + + void SdHostStandardController::DisableInterruptStatus() { + /* If we're using interrupt events, configure appropriately. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Disable the interrupt signals. */ + reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); + reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); + } + #endif + + /* Mask the status register interrupt enables. */ + reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); + reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + Result SdHostStandardController::WaitInterrupt(u32 timeout_ms) { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait for the interrupt to be signaled. */ + os::WaitableHolderType *signaled_holder = os::TimedWaitAny(std::addressof(this->waitable_manager), TimeSpan::FromMilliSeconds(timeout_ms)); + if (signaled_holder == std::addressof(this->interrupt_event_holder)) { + /* We received the interrupt. */ + return ResultSuccess(); + } else if (signaled_holder == std::addressof(this->removed_event_holder)) { + /* The device was removed. */ + return sdmmc::ResultDeviceRemoved(); + } else { + /* Timeout occurred. */ + return sdmmc::ResultWaitInterruptSoftwareTimeout(); + } + } + + void SdHostStandardController::ClearInterrupt() { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Clear the interrupt event. */ + os::ClearInterruptEvent(this->interrupt_event); + } + #endif + + void SdHostStandardController::SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data) { + /* Ensure the transfer data is valid. */ + AMS_ABORT_UNLESS(xfer_data->block_size != 0); + AMS_ABORT_UNLESS(xfer_data->num_blocks != 0); + + /* Determine the number of blocks. */ + const u16 num_blocks = std::min(xfer_data->num_blocks, SdHostStandardRegisters::BlockCountMax); + + /* Determine the address/how many blocks to transfer. */ + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + const u64 address = this->GetDeviceVirtualAddress(reinterpret_cast(xfer_data->buffer), xfer_data->block_size * num_blocks); + const u16 num_xfer_blocks = num_blocks; + #else + const u64 address = reinterpret_cast(xfer_data->buffer); + const u16 num_xfer_blocks = num_blocks; + #endif + + /* Verify the address is usable. */ + AMS_ABORT_UNLESS(util::IsAligned(address, BufferDeviceVirtualAddressAlignment)); + + /* Configure for sdma. */ + reg::Write(this->registers->adma_address, static_cast(address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(address >> BITSIZEOF(u32))); + + /* Set our next sdma address. */ + this->next_sdma_address = util::AlignDown(address + SdmaBufferBoundary, SdmaBufferBoundary); + + /* Configure block size. */ + AMS_ABORT_UNLESS(xfer_data->block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); + reg::Write(this->registers->block_size, SD_REG_BITS_ENUM (BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 512_KB), + SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, static_cast(xfer_data->block_size))); + + /* Configure transfer blocks. */ + reg::Write(this->registers->block_count, num_xfer_blocks); + if (out_num_transferred_blocks != nullptr) { + *out_num_transferred_blocks = num_xfer_blocks; + } + + /* Configure transfer mode. */ + reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM (TRANSFER_MODE_DMA_ENABLE, ENABLE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_BLOCK_COUNT_ENABLE, (xfer_data->is_multi_block_transfer), ENABLE, DISABLE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_MULTI_BLOCK_SELECT, (xfer_data->is_multi_block_transfer), MULTI_BLOCK, SINGLE_BLOCK), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, (xfer_data->transfer_direction == TransferDirection_ReadFromDevice), READ, WRITE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_AUTO_CMD_ENABLE, (xfer_data->is_stop_transmission_command_enabled), CMD12_ENABLE, DISABLE)); + } + + void SdHostStandardController::SetTransferForTuning() { + /* Get the tuning block size. */ + u16 tuning_block_size; + switch (this->GetBusWidth()) { + case BusWidth_4Bit: + tuning_block_size = 64; + break; + case BusWidth_8Bit: + tuning_block_size = 128; + break; + case BusWidth_1Bit: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Configure block size. */ + AMS_ABORT_UNLESS(tuning_block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); + reg::Write(this->registers->block_size, SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, tuning_block_size)); + + /* Configure transfer blocks. */ + reg::Write(this->registers->block_count, 1); + + /* Configure transfer mode. */ + reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, READ)); + } + + void SdHostStandardController::SetCommand(const Command *command, bool has_xfer_data) { + /* Encode the command value. */ + u16 command_val = 0; + + /* Encode the response type. */ + switch (command->response_type) { + case ResponseType_R0: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, NO_RESPONSE), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + case ResponseType_R1: + case ResponseType_R6: + case ResponseType_R7: + command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_RESPONSE_TYPE, command->is_busy, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE, RESPONSE_LENGTH_48), + SD_REG_BITS_ENUM (COMMAND_CRC_CHECK, ENABLE), + SD_REG_BITS_ENUM (COMMAND_INDEX_CHECK, ENABLE)); + break; + case ResponseType_R2: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_136), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, ENABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + case ResponseType_R3: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_48), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Encode the data present select. */ + command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_DATA_PRESENT, has_xfer_data, DATA_PRESENT, NO_DATA_PRESENT)); + + /* Encode the command index. */ + AMS_ABORT_UNLESS(command->command_index <= SdHostStandardCommandIndexMax); + command_val |= reg::Encode(SD_REG_BITS_VALUE(COMMAND_COMMAND_INDEX, command->command_index)); + + /* Write the command and argument. */ + reg::Write(this->registers->argument, command->command_argument); + reg::Write(this->registers->command, command_val); + } + + void SdHostStandardController::SetCommandForTuning(u32 command_index) { + Command command(command_index, 0, ResponseType_R1, false); + return this->SetCommand(std::addressof(command), true); + } + + Result SdHostStandardController::ResetCmdDatLine() { + /* Set the software reset cmd/dat bits. */ + reg::ReadWrite(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, RESET), + SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, RESET)); + + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait until reset is done. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, WORK), + SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, WORK))) + { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + return sdmmc::ResultAbortTransactionSoftwareTimeout(); + } + } + } + + return ResultSuccess(); + } + + Result SdHostStandardController::AbortTransaction() { + R_TRY(this->ResetCmdDatLine()); + return ResultSuccess(); + } + + Result SdHostStandardController::WaitWhileCommandInhibit(bool has_dat) { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while command inhibit cmd is set. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, READY))) { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandInhibitCmdSoftwareTimeout(); + } + } + } + + /* Wait while command inhibit dat is set. */ + if (has_dat) { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, READY))) { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandInhibitDatSoftwareTimeout(); + } + } + } + + return ResultSuccess(); + } + + Result SdHostStandardController::CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask) { + /* Read the statuses. */ + volatile u16 normal_int_status = reg::Read(this->registers->normal_int_status); + volatile u16 error_int_status = reg::Read(this->registers->error_int_status); + volatile u16 auto_cmd_err_status = reg::Read(this->registers->acmd12_err); + + /* Set the output status, if necessary. */ + if (out_normal_int_status != nullptr) { + *out_normal_int_status = normal_int_status; + } + + /* If we don't have an error interrupt, just use the normal status. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, NO_ERROR))) { + /* If the wait mask has any bits set, we're done waiting for the interrupt. */ + const u16 masked_status = (normal_int_status & wait_mask); + R_UNLESS(masked_status != 0, sdmmc::ResultNoWaitedInterrupt()); + + /* Write the masked value to the status register to ensure consistent state. */ + reg::Write(this->registers->normal_int_status, masked_status); + return ResultSuccess(); + } + + /* We have an error interrupt. Write the status to the register to ensure consistent state. */ + reg::Write(this->registers->error_int_status, error_int_status); + + /* Check the error interrupt status bits, and return appropriate errors. */ + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, NO_ERROR)), sdmmc::ResultResponseIndexError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, NO_ERROR)), sdmmc::ResultResponseEndBitError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, NO_ERROR)), sdmmc::ResultResponseCrcError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, NO_ERROR)), sdmmc::ResultResponseTimeoutError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, NO_ERROR)), sdmmc::ResultDataEndBitError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, NO_ERROR)), sdmmc::ResultDataCrcError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, NO_ERROR)), sdmmc::ResultDataTimeoutError()); + + /* Check for auto cmd errors. */ + if (reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, ERROR))) { + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, NO_ERROR)), sdmmc::ResultAutoCommandResponseIndexError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, NO_ERROR)), sdmmc::ResultAutoCommandResponseEndBitError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, NO_ERROR)), sdmmc::ResultAutoCommandResponseCrcError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, NO_ERROR)), sdmmc::ResultAutoCommandResponseTimeoutError()); + + /* An known auto cmd error occurred. */ + return sdmmc::ResultSdHostStandardUnknownAutoCmdError(); + } else { + /* Unknown error occurred. */ + return sdmmc::ResultSdHostStandardUnknownError(); + } + } + + Result SdHostStandardController::WaitCommandComplete() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Wait for interrupt. */ + Result result = this->WaitInterrupt(CommandTimeoutMilliSeconds); + if (R_SUCCEEDED(result)) { + /* If we succeeded, check/clear our interrupt status. */ + result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); + this->ClearInterrupt(); + + if (R_FAILED(result)) { + this->AbortTransaction(); + } + + return result; + } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { + /* Otherwise, check if the device was removed. */ + return result; + } else { + /* If the device wasn't removed, cancel our transaction. */ + this->AbortTransaction(); + return sdmmc::ResultCommandCompleteSoftwareTimeout(); + } + } + #else + { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while command is not complete. */ + { + ManualTimer timer(CommandTimeoutMilliSeconds); + while (true) { + /* Check and clear the interrupt status. */ + const auto result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); + + /* If we succeeded, we're done. */ + if (R_SUCCEEDED(result)) { + return ResultSuccess(); + } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { + /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandCompleteSoftwareTimeout(); + } + } else { + /* Otherwise, we have a generic failure. */ + this->AbortTransaction(); + return result; + } + } + } + } + #endif + } + + Result SdHostStandardController::WaitTransferComplete() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Wait while transfer is not complete. */ + while (true) { + /* Get the last block count. */ + const u16 last_block_count = reg::Read(this->registers->block_count); + + /* Wait for interrupt. */ + Result result = this->WaitInterrupt(this->check_transfer_interval_ms); + if (R_SUCCEEDED(result)) { + /* If we succeeded, check/clear our interrupt status. */ + volatile u16 normal_int_status; + result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); + + this->ClearInterrupt(); + + /* If the interrupt succeeded, check status. */ + if (R_SUCCEEDED(result)) { + /* If the transfer is complete, we're done. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { + return ResultSuccess(); + } + + /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { + reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); + + this->next_sdma_address += SdmaBufferBoundary; + } + } else { + /* Abort the transaction. */ + this->AbortTransaction(); + return result; + } + + return result; + } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { + /* Otherwise, check if the device was removed. */ + return result; + } else { + /* Otherwise, timeout if the transfer hasn't advanced. */ + if (last_block_count != reg::Read(this->registers->block_count)) { + this->AbortTransaction(); + return sdmmc::ResultTransferCompleteSoftwareTimeout(); + } + } + } + } + #else + { + /* Wait while transfer is not complete. */ + while (true) { + /* Get the last block count. */ + const u16 last_block_count = reg::Read(this->registers->block_count); + + /* Wait until transfer times out. */ + { + ManualTimer timer(this->check_transfer_interval_ms); + while (true) { + /* Check/clear our interrupt status. */ + volatile u16 normal_int_status; + const auto result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); + + /* If the check succeeded, check status. */ + if (R_SUCCEEDED(result)) { + /* If the transfer is complete, we're done. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { + return ResultSuccess(); + } + + /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { + reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); + + this->next_sdma_address += SdmaBufferBoundary; + } + } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { + /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ + if (!timer.Update()) { + /* Only timeout if the transfer hasn't advanced. */ + if (last_block_count != reg::Read(this->registers->block_count)) { + this->AbortTransaction(); + return sdmmc::ResultTransferCompleteSoftwareTimeout(); + } + break; + } + } else { + /* Otherwise, we have a generic failure. */ + this->AbortTransaction(); + return result; + } + } + } + } + } + #endif + } + + Result SdHostStandardController::WaitWhileBusy() { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while busy. */ + { + ManualTimer timer(BusyTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* If the DAT0 line signal is level high, we're done. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, HIGH))) { + return ResultSuccess(); + } + + /* Otherwise, check if we're timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultBusySoftwareTimeout(); + } + } + } + } + + void SdHostStandardController::GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { + /* Check that we can write the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + + /* Get the response appropriately. */ + switch (response_type) { + case ResponseType_R1: + case ResponseType_R3: + case ResponseType_R6: + case ResponseType_R7: + /* 32-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); + out_response[0] = reg::Read(this->registers->response[0]); + break; + case ResponseType_R2: + /* 128-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); + out_response[0] = reg::Read(this->registers->response[0]); + out_response[1] = reg::Read(this->registers->response[1]); + out_response[2] = reg::Read(this->registers->response[2]); + out_response[3] = reg::Read(this->registers->response[3]); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result SdHostStandardController::IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* Wait until we can issue the command. */ + R_TRY(this->WaitWhileCommandInhibit((xfer_data != nullptr) || command->is_busy)); + + /* Configure for the transfer. */ + u32 num_transferred_blocks = 0; + if (xfer_data != nullptr) { + /* Setup the transfer, and get the number of blocks. */ + this->SetTransfer(std::addressof(num_transferred_blocks), xfer_data); + + /* Ensure the device sees consistent data with the cpu. */ + dd::FlushDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); + } + + /* Issue the command with interrupt status enabled. */ + { + this->EnableInterruptStatus(); + ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; + + /* Set the command. */ + this->SetCommand(command, xfer_data != nullptr); + + /* Wait for the command to complete. */ + R_TRY(this->WaitCommandComplete()); + + /* Process any response. */ + if (command->response_type != ResponseType_R0) { + this->last_response_type = command->response_type; + this->GetResponse(this->last_response, sizeof(this->last_response), this->last_response_type); + } + + /* Wait for data to be transferred. */ + if (xfer_data != nullptr) { + R_TRY(this->WaitTransferComplete()); + } + } + + /* If data was transferred, ensure we're in a consistent state. */ + if (xfer_data != nullptr) { + /* Ensure the cpu sees consistent data with the device. */ + if (xfer_data->transfer_direction == TransferDirection_ReadFromDevice) { + dd::InvalidateDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); + } + + /* Set the number of transferred blocks. */ + if (out_num_transferred_blocks != nullptr) { + *out_num_transferred_blocks = num_transferred_blocks; + } + + /* Process stop transition command. */ + this->last_stop_transmission_response = this->registers->response[3]; + } + + /* Wait until we're no longer busy. */ + if (command->is_busy || (xfer_data != nullptr)) { + R_TRY(this->WaitWhileBusy()); + } + + return ResultSuccess(); + } + + Result SdHostStandardController::IssueStopTransmissionCommandWithDeviceClock(u32 *out_response) { + /* Wait until we can issue the command. */ + R_TRY(this->WaitWhileCommandInhibit(false)); + + /* Issue the command with interrupt status enabled. */ + constexpr ResponseType StopTransmissionCommandResponseType = ResponseType_R1; + { + this->EnableInterruptStatus(); + ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; + + /* Set the command. */ + Command command(CommandIndex_StopTransmission, 0, StopTransmissionCommandResponseType, true); + this->SetCommand(std::addressof(command), false); + + /* Wait for the command to complete. */ + R_TRY(this->WaitCommandComplete()); + } + + /* Process response. */ + this->GetResponse(out_response, sizeof(u32), StopTransmissionCommandResponseType); + + /* Wait until we're done. */ + R_TRY(this->WaitWhileBusy()); + + return ResultSuccess(); + } + + SdHostStandardController::SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size) { + /* Translate the physical address to a address. */ + const uintptr_t registers_addr = dd::QueryIoMapping(registers_phys_addr, registers_size); + + /* Set registers. */ + AMS_ABORT_UNLESS(registers_addr != 0); + this->registers = reinterpret_cast(registers_addr); + + /* Reset DMA buffers, if we have any. */ + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + this->ResetBufferInfos(); + #endif + + /* Clear removed event, if we have one. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + this->removed_event = nullptr; + #endif + + /* Clear dma address. */ + this->next_sdma_address = 0; + this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; + + /* Clear clock/power trackers. */ + this->device_clock_frequency_khz = 0; + this->is_power_saving_enable = false; + this->is_device_clock_enable = false; + + /* Clear last response. */ + this->last_response_type = ResponseType_R0; + std::memset(this->last_response, 0, sizeof(this->last_response)); + this->last_stop_transmission_response = 0; + } + + void SdHostStandardController::Initialize() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::InitializeWaitableManager(std::addressof(this->waitable_manager)); + + AMS_ABORT_UNLESS(this->interrupt_event != nullptr); + os::InitializeWaitableHolder(std::addressof(this->interrupt_event_holder), this->interrupt_event); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->interrupt_event_holder)); + + if (this->removed_event != nullptr) { + os::InitializeWaitableHolder(std::addressof(this->removed_event_holder), this->removed_event); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->removed_event_holder)); + } + } + #endif + } + + void SdHostStandardController::Finalize() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + if (this->removed_event != nullptr) { + os::UnlinkWaitableHolder(std::addressof(this->removed_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->removed_event_holder)); + } + + os::UnlinkWaitableHolder(std::addressof(this->interrupt_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->interrupt_event_holder)); + + os::FinalizeWaitableManager(std::addressof(this->waitable_manager)); + } + #endif + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void SdHostStandardController::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Find and set a free info. */ + for (auto &info : this->buffer_infos) { + if (info.buffer_address == 0) { + info = { + .buffer_address = buffer, + .buffer_size = buffer_size, + .buffer_device_virtual_address = buffer_device_virtual_address, + }; + return; + } + } + + AMS_ABORT("Out of BufferInfos\n"); + } + + void SdHostStandardController::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Find and clear the buffer info. */ + for (auto &info : this->buffer_infos) { + if (info.buffer_address == 0) { + AMS_ABORT_UNLESS(info.buffer_size == buffer_size); + AMS_ABORT_UNLESS(info.buffer_device_virtual_address == buffer_device_virtual_address); + + info.buffer_address = 0; + info.buffer_size = 0; + return; + } + } + + AMS_ABORT("BufferInfo not found\n"); + } + #endif + + void SdHostStandardController::SetWorkBuffer(void *wb, size_t wb_size) { + AMS_UNUSED(wb, wb_size); + AMS_ABORT("WorkBuffer is not needed\n"); + } + + BusPower SdHostStandardController::GetBusPower() const { + /* Check if the bus has power. */ + if (reg::HasValue(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON))) { + /* If it does, return the corresponding power. */ + switch (reg::GetValue(this->registers->power_control, SD_REG_BITS_MASK(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1))) { + case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_1_8V: + return BusPower_1_8V; + case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_3_3V: + return BusPower_3_3V; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } else { + /* It doesn't, so it's off. */ + return BusPower_Off; + } + } + + void SdHostStandardController::SetBusWidth(BusWidth bus_width) { + /* Check that we support the bus width. */ + AMS_ABORT_UNLESS(this->IsSupportedBusWidth(bus_width)); + + /* Set the appropriate data transfer width. */ + switch (bus_width) { + case BusWidth_1Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, ONE_BIT)); + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); + break; + case BusWidth_4Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT)); + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); + break; + case BusWidth_8Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + BusWidth SdHostStandardController::GetBusWidth() const { + /* Check if the bus is using eight-bit extended data transfer. */ + if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT))) { + return BusWidth_8Bit; + } else { + /* Bus is configured as USE_DATA_TRANSFER_WIDTH, so check if it's four bit. */ + if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT))) { + return BusWidth_4Bit; + } else { + return BusWidth_1Bit; + } + } + } + + void SdHostStandardController::SetPowerSaving(bool en) { + /* Set whether we're power saving enable. */ + this->is_power_saving_enable = en; + + /* Configure accordingly. */ + if (this->is_power_saving_enable) { + /* We want to disable SD clock if it's enabled. */ + if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + } else { + /* We want to enable SD clock if it's disabled and we're supposed to enable device clock. */ + if (this->is_device_clock_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + } + } + + void SdHostStandardController::EnableDeviceClock() { + /* If we're not in power-saving mode and the device clock is disabled, enable it. */ + if (!this->is_power_saving_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + this->is_device_clock_enable = true; + } + + void SdHostStandardController::DisableDeviceClock() { + /* Unconditionally disable the device clock. */ + this->is_device_clock_enable = false; + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + void SdHostStandardController::ChangeCheckTransferInterval(u32 ms) { + this->check_transfer_interval_ms = ms; + } + + void SdHostStandardController::SetDefaultCheckTransferInterval() { + this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; + } + + Result SdHostStandardController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* We need to have device clock enabled to issue commands. */ + AMS_ABORT_UNLESS(this->is_device_clock_enable); + + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that our configuration takes. */ + this->EnsureControl(); + + /* Wait 8 device clocks to be sure that it's usable. */ + WaitClocks(8, this->device_clock_frequency_khz); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Issue the command. */ + { + /* After we issue the command, we need to wait 8 device clocks. */ + ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; + + return this->IssueCommandWithDeviceClock(command, xfer_data, out_num_transferred_blocks); + } + } + + Result SdHostStandardController::IssueStopTransmissionCommand(u32 *out_response) { + /* We need to have device clock enabled to issue commands. */ + AMS_ABORT_UNLESS(this->is_device_clock_enable); + + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that our configuration takes. */ + this->EnsureControl(); + + /* Wait 8 device clocks to be sure that it's usable. */ + WaitClocks(8, this->device_clock_frequency_khz); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Issue the command. */ + { + /* After we issue the command, we need to wait 8 device clocks. */ + ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; + + return this->IssueStopTransmissionCommandWithDeviceClock(out_response); + } + } + + void SdHostStandardController::GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { + /* Check that we can get the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + AMS_ABORT_UNLESS(response_type == this->last_response_type); + + /* Get the response appropriately. */ + switch (response_type) { + case ResponseType_R1: + case ResponseType_R3: + case ResponseType_R6: + case ResponseType_R7: + /* 32-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); + out_response[0] = this->last_response[0]; + break; + case ResponseType_R2: + /* 128-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); + out_response[0] = this->last_response[0]; + out_response[1] = this->last_response[1]; + out_response[2] = this->last_response[2]; + out_response[3] = this->last_response[3]; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SdHostStandardController::GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const { + /* Check that we can get the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + AMS_ABORT_UNLESS(response_size >= sizeof(u32)); + + /* Get the response. */ + out_response[0] = this->last_stop_transmission_response; + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.hpp new file mode 100644 index 0000000..7bdf0a3 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_controller.hpp @@ -0,0 +1,188 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_sd_host_standard_registers.hpp" + +namespace ams::sdmmc::impl { + + class SdHostStandardController : public IHostController { + protected: + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + struct BufferInfo { + uintptr_t buffer_address; + size_t buffer_size; + dd::DeviceVirtualAddress buffer_device_virtual_address; + }; + + static constexpr inline auto NumBufferInfos = 3; + #endif + protected: + SdHostStandardRegisters *registers; + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + BufferInfo buffer_infos[NumBufferInfos]; + #endif + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + os::WaitableManagerType waitable_manager; + os::InterruptEventType *interrupt_event; + os::WaitableHolderType interrupt_event_holder; + os::EventType *removed_event; + os::WaitableHolderType removed_event_holder; + #endif + + u64 next_sdma_address; + u32 check_transfer_interval_ms; + + u32 device_clock_frequency_khz; + bool is_power_saving_enable; + bool is_device_clock_enable; + + ResponseType last_response_type; + u32 last_response[4]; + u32 last_stop_transmission_response; + protected: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + void PreSetInterruptEvent(os::InterruptEventType *ie) { + this->interrupt_event = ie; + } + + bool IsRemoved() const { + return this->removed_event != nullptr && os::TryWaitEvent(this->removed_event); + } + #endif + + void SetDeviceClockFrequencyKHz(u32 khz) { + this->device_clock_frequency_khz = khz; + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void ResetBufferInfos(); + dd::DeviceVirtualAddress GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size); + #endif + + void EnsureControl(); + Result EnableInternalClock(); + void SetBusPower(BusPower bus_power); + + void EnableInterruptStatus(); + void DisableInterruptStatus(); + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + Result WaitInterrupt(u32 timeout_ms); + void ClearInterrupt(); + #endif + + void SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data); + void SetTransferForTuning(); + + void SetCommand(const Command *command, bool has_xfer_data); + void SetCommandForTuning(u32 command_index); + + Result ResetCmdDatLine(); + Result AbortTransaction(); + Result WaitWhileCommandInhibit(bool has_dat); + Result CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask); + Result WaitCommandComplete(); + Result WaitTransferComplete(); + Result WaitWhileBusy(); + + void GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const; + + Result IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks); + Result IssueStopTransmissionCommandWithDeviceClock(u32 *out_response); + + ALWAYS_INLINE Result CheckRemoved() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved()); + #endif + + return ResultSuccess(); + } + public: + SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size); + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual void PreSetRemovedEvent(os::EventType *e) override { + this->removed_event = e; + } + #endif + + virtual void Initialize() override; + virtual void Finalize() override; + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; + virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; + #endif + + virtual void SetWorkBuffer(void *wb, size_t wb_size) override; + + virtual Result SwitchToSdr12() override { + AMS_ABORT("SwitchToSdr12 not supported\n"); + } + + virtual BusPower GetBusPower() const override; + + virtual void SetBusWidth(BusWidth bus_width) override; + virtual BusWidth GetBusWidth() const override; + + virtual u32 GetDeviceClockFrequencyKHz() const override { + return this->device_clock_frequency_khz; + } + + virtual void SetPowerSaving(bool en) override; + virtual bool IsPowerSavingEnable() const override { + return this->is_power_saving_enable; + } + + virtual void EnableDeviceClock() override; + virtual void DisableDeviceClock() override; + virtual bool IsDeviceClockEnable() const override { + return this->is_device_clock_enable; + } + + virtual u32 GetMaxTransferNumBlocks() const override { + return SdHostStandardRegisters::BlockCountMax; + } + + virtual void ChangeCheckTransferInterval(u32 ms) override; + virtual void SetDefaultCheckTransferInterval() override; + + virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; + virtual Result IssueStopTransmissionCommand(u32 *out_response) override; + + virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const override; + virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const override; + + virtual bool IsSupportedTuning() const override { + return false; + } + + virtual Result Tuning(SpeedMode speed_mode, u32 command_index) { + AMS_UNUSED(speed_mode, command_index); + AMS_ABORT("Tuning not supported\n"); + } + + virtual void SaveTuningStatusForHs400() { + AMS_ABORT("SaveTuningStatusForHs400 not supported\n"); + } + + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_registers.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_registers.hpp new file mode 100644 index 0000000..e104374 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sd_host_standard_registers.hpp @@ -0,0 +1,193 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + struct SdHostStandardRegisters { + volatile uint32_t dma_address; + volatile uint16_t block_size; + volatile uint16_t block_count; + volatile uint32_t argument; + volatile uint16_t transfer_mode; + volatile uint16_t command; + volatile uint32_t response[0x4]; + volatile uint32_t buffer; + volatile uint32_t present_state; + volatile uint8_t host_control; + volatile uint8_t power_control; + volatile uint8_t block_gap_control; + volatile uint8_t wake_up_control; + volatile uint16_t clock_control; + volatile uint8_t timeout_control; + volatile uint8_t software_reset; + volatile uint16_t normal_int_status; + volatile uint16_t error_int_status; + volatile uint16_t normal_int_enable; + volatile uint16_t error_int_enable; + volatile uint16_t normal_signal_enable; + volatile uint16_t error_signal_enable; + volatile uint16_t acmd12_err; + volatile uint16_t host_control2; + volatile uint32_t capabilities; + volatile uint32_t capabilities_1; + volatile uint32_t max_current; + volatile uint32_t _0x4c; + volatile uint16_t set_acmd12_error; + volatile uint16_t set_int_error; + volatile uint8_t adma_error; + volatile uint8_t _0x56[0x3]; + volatile uint32_t adma_address; + volatile uint32_t upper_adma_address; + volatile uint16_t preset_for_init; + volatile uint16_t preset_for_default; + volatile uint16_t preset_for_high; + volatile uint16_t preset_for_sdr12; + volatile uint16_t preset_for_sdr25; + volatile uint16_t preset_for_sdr50; + volatile uint16_t preset_for_sdr104; + volatile uint16_t preset_for_ddr50; + volatile uint32_t _0x70[0x23]; + volatile uint16_t slot_int_status; + volatile uint16_t host_version; + + static constexpr inline u16 BlockCountMax = 0xFFFF; + }; + static_assert(sizeof(SdHostStandardRegisters) == 0x100); + + constexpr inline size_t SdmaBufferBoundary = 512_KB; + + #define SD_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SD_HOST_STANDARD, NAME) + #define SD_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SD_HOST_STANDARD, NAME, VALUE) + #define SD_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SD_HOST_STANDARD, NAME, ENUM) + #define SD_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SD_HOST_STANDARD, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_SD_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SD_HOST_STANDARD, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_SD_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_SD_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_SD_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_SD_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 (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_SD_REG(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, 0, 12); + DEFINE_SD_REG_THREE_BIT_ENUM(BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 12, 4_KB, 8_KB, 16_KB, 32_KB, 64_KB, 128_KB, 256_KB, 512_KB); + constexpr inline size_t SdHostStandardBlockSizeTransferBlockSizeMax = 0xFFF; + + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DMA_ENABLE, 0, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_BLOCK_COUNT_ENABLE, 1, DISABLE, ENABLE); + DEFINE_SD_REG_TWO_BIT_ENUM(TRANSFER_MODE_AUTO_CMD_ENABLE, 2, DISABLE, CMD12_ENABLE, CMD23_ENABLE, AUTO_SELECT); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, 4, WRITE, READ); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_MULTI_BLOCK_SELECT, 5, SINGLE_BLOCK, MULTI_BLOCK); + + DEFINE_SD_REG_TWO_BIT_ENUM(COMMAND_RESPONSE_TYPE, 0, NO_RESPONSE, RESPONSE_LENGTH_136, RESPONSE_LENGTH_48, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_CRC_CHECK, 3, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_INDEX_CHECK, 4, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_DATA_PRESENT, 5, NO_DATA_PRESENT, DATA_PRESENT); + DEFINE_SD_REG(COMMAND_COMMAND_INDEX, 8, 6); + constexpr inline size_t SdHostStandardCommandIndexMax = 0x3F; + + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, 0, READY, NOT_READY); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, 1, READY, NOT_READY); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, 20, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT1_LINE_SIGNAL_LEVEL, 21, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT2_LINE_SIGNAL_LEVEL, 22, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT3_LINE_SIGNAL_LEVEL, 23, LOW, HIGH); + DEFINE_SD_REG(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 20, 4); + + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, 1, ONE_BIT, FOUR_BIT); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, 2, NORMAL_SPEED, HIGH_SPEED); + DEFINE_SD_REG_TWO_BIT_ENUM(HOST_CONTROL_DMA_SELECT, 3, SDMA, RESERVED1, ADMA2, ADMA2_OR_ADMA3); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, 5, USE_DATA_TRANSFER_WIDTH, EIGHT_BIT); + + DEFINE_SD_REG_BIT_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, 0, OFF, ON); + DEFINE_SD_REG_THREE_BIT_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1, RESERVED0, RESERVED1, RESERVED2, RESERVED3, RESERVED4, 1_8V, 3_0V, 3_3V); + + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, 0, STOP, OSCILLATE); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, 1, NOT_READY, READY); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, 2, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, 5, DIVIDED_CLOCK, PROGRAMMABLE_CLOCK); + DEFINE_SD_REG(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, 6, 2); + DEFINE_SD_REG(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, 8, 8); + + DEFINE_SD_REG(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0, 4); + + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_ALL, 0, WORK, RESET); + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_CMD, 1, WORK, RESET); + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_DAT, 2, WORK, RESET); + + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, 0, NOT_COMPLETE, COMPLETE); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, 1, NOT_COMPLETE, COMPLETE); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, 3, NOT_GENERATED, GENERATED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, 15, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, 0, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, 1, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, 2, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, 3, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, 4, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, 5, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, 6, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, 8, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, 1, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, 2, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, 3, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, 4, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, 0, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, 1, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, 3, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, 5, MASKED, ENABLED); + + #define SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, __ENUM__), \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, __ENUM__), \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, __ENUM__) + + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, 1, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, 5, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, 6, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, 8, MASKED, ENABLED); + + #define SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, __ENUM__) + + + DEFINE_SD_REG_THREE_BIT_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, 0, SDR12, SDR25, SDR50, SDR104, DDR50, HS400, RSVD6, UHS_II); + + constexpr inline auto SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_HS200 = SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_SDR104; + + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3, 3_3V_SIGNALING, 1_8V_SIGNALING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_EXECUTE_TUNING, 6, TUNING_COMPLETED, EXECUTE_TUNING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, 7, USING_FIXED_CLOCK, USING_TUNED_CLOCK); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, 12, VERSION_300_COMPATIBLE, VERSION_4); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 13, 32_BIT_ADDRESSING, 64_BIT_ADDRESSING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, 15, HOST_DRIVER, AUTOMATIC_SELECTION); + + DEFINE_SD_REG_BIT_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, 28, NOT_SUPPORTED, SUPPORTED); + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp new file mode 100644 index 0000000..3b16008 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp @@ -0,0 +1,1311 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" +#include "sdmmc_io_impl.board.nintendo_nx.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + /* FOR REFERENCE: board-specific sdmmc registers. */ + //struct SdmmcRegisters { + // /* Standard registers. */ + // volatile SdHostStandardRegisters sd_host_standard_registers; + // + // /* Vendor specific registers */ + // volatile uint32_t vendor_clock_cntrl; + // volatile uint32_t vendor_sys_sw_cntrl; + // volatile uint32_t vendor_err_intr_status; + // volatile uint32_t vendor_cap_overrides; + // volatile uint32_t vendor_boot_cntrl; + // volatile uint32_t vendor_boot_ack_timeout; + // volatile uint32_t vendor_boot_dat_timeout; + // volatile uint32_t vendor_debounce_count; + // volatile uint32_t vendor_misc_cntrl; + // volatile uint32_t max_current_override; + // volatile uint32_t max_current_override_hi; + // volatile uint32_t _0x12c[0x20]; + // volatile uint32_t vendor_io_trim_cntrl; + // + // /* Start of sdmmc2/sdmmc4 only */ + // volatile uint32_t vendor_dllcal_cfg; + // volatile uint32_t vendor_dll_ctrl0; + // volatile uint32_t vendor_dll_ctrl1; + // volatile uint32_t vendor_dllcal_cfg_sta; + // /* End of sdmmc2/sdmmc4 only */ + // + // volatile uint32_t vendor_tuning_cntrl0; + // volatile uint32_t vendor_tuning_cntrl1; + // volatile uint32_t vendor_tuning_status0; + // volatile uint32_t vendor_tuning_status1; + // volatile uint32_t vendor_clk_gate_hysteresis_count; + // volatile uint32_t vendor_preset_val0; + // volatile uint32_t vendor_preset_val1; + // volatile uint32_t vendor_preset_val2; + // volatile uint32_t sdmemcomppadctrl; + // volatile uint32_t auto_cal_config; + // volatile uint32_t auto_cal_interval; + // volatile uint32_t auto_cal_status; + // volatile uint32_t io_spare; + // volatile uint32_t sdmmca_mccif_fifoctrl; + // volatile uint32_t timeout_wcoal_sdmmca; + // volatile uint32_t _0x1fc; + //}; + + DEFINE_SD_REG_BIT_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, 2, NORMAL, OVERRIDE); + DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TAP_VAL, 16, 8); + DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TRIM_VAL, 24, 5); + + DEFINE_SD_REG(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 8, 6); + + DEFINE_SD_REG(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 2, 1); + + DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, 31, DISABLE, ENABLE); + + DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, 31, DONE, RUNNING); + + DEFINE_SD_REG(VENDOR_TUNING_CNTRL0_MUL_M, 6, 7); + DEFINE_SD_REG_THREE_BIT_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, 13, TRIES_40, TRIES_64, TRIES_128, TRIES_192, TRIES_256, RESERVED5, RESERVED6, RESERVED7); + DEFINE_SD_REG_BIT_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, 17, NOT_UPDATED_BY_HW, UPDATED_BY_HW); + + DEFINE_SD_REG(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, 0, 4); + DEFINE_SD_REG(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 31, 1); + + DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, 0, 7); + DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, 8, 7); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, 29, DISABLED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLED, ENABLED); + + DEFINE_SD_REG(AUTO_CAL_STATUS_AUTO_CAL_PULLUP, 0, 7); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, 31, INACTIVE, ACTIVE); + + DEFINE_SD_REG_BIT_ENUM(IO_SPARE_SPARE_OUT_3, 19, TWO_CYCLE_DELAY, ONE_CYCLE_DELAY); + + namespace { + + constexpr inline u32 TuningCommandTimeoutMilliSeconds = 5; + + constexpr void GetDividerSetting(u32 *out_target_clock_frequency_khz, u16 *out_x, SpeedMode speed_mode) { + switch (speed_mode) { + case SpeedMode_MmcIdentification: + *out_target_clock_frequency_khz = 26000; + *out_x = 66; + break; + case SpeedMode_MmcLegacySpeed: + *out_target_clock_frequency_khz = 26000; + *out_x = 1; + break; + case SpeedMode_MmcHighSpeed: + *out_target_clock_frequency_khz = 52000; + *out_x = 1; + break; + case SpeedMode_MmcHs200: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_MmcHs400: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_SdCardIdentification: + *out_target_clock_frequency_khz = 25000; + *out_x = 64; + break; + case SpeedMode_SdCardDefaultSpeed: + *out_target_clock_frequency_khz = 25000; + *out_x = 1; + break; + case SpeedMode_SdCardHighSpeed: + *out_target_clock_frequency_khz = 50000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr12: + *out_target_clock_frequency_khz = 25000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr50: + *out_target_clock_frequency_khz = 100000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr104: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_GcAsicFpgaSpeed: + *out_target_clock_frequency_khz = 40800; + *out_x = 1; + break; + case SpeedMode_GcAsicSpeed: + *out_target_clock_frequency_khz = 200000; + *out_x = 2; + break; + case SpeedMode_SdCardSdr25: + case SpeedMode_SdCardDdr50: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + namespace { + + #if defined(AMS_SDMMC_THREAD_SAFE) + constinit os::SdkMutex g_soc_mutex; + + #define AMS_SDMMC_LOCK_SOC_MUTEX() std::scoped_lock lk(g_soc_mutex) + + #else + + #define AMS_SDMMC_LOCK_SOC_MUTEX() + + #endif + + constinit bool g_determined_soc = false; + constinit bool g_is_soc_mariko = false; + + } + + bool IsSocMariko() { + if (!g_determined_soc) { + /* Ensure we have exclusive access to the soc variables. */ + AMS_SDMMC_LOCK_SOC_MUTEX(); + + /* Check the SocType. */ + #if defined(ATMOSPHERE_IS_EXOSPHERE) + { + g_is_soc_mariko = fuse::GetSocType() == fuse::SocType_Mariko; + } + #elif defined(ATMOSPHERE_IS_MESOSPHERE) + { + MESOSPHERE_TODO("Detect mariko via KSystemControl call?"); + } + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + { + /* Connect to spl for the duration of our check. */ + spl::Initialize(); + ON_SCOPE_EXIT { spl::Finalize(); }; + + g_is_soc_mariko = spl::GetSocType() == spl::SocType_Mariko; + } + #else + #error "Unknown execution context for ams::sdmmc::impl::IsSocMariko" + #endif + + /* Note that we determined the soc. */ + g_determined_soc = true; + } + + return g_is_soc_mariko; + } + + void SdmmcController::ReleaseReset(SpeedMode speed_mode) { + /* Get the clock reset module. */ + const auto module = this->GetClockResetModule(); + + /* If the module is available, disable clock. */ + if (ClockResetController::IsAvailable(module)) { + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::EnsureControl(); + } + + /* Get the correct divider setting for the speed mode. */ + u32 target_clock_frequency_khz; + u16 x; + GetDividerSetting(std::addressof(target_clock_frequency_khz), std::addressof(x), speed_mode); + + /* Release reset. */ + ClockResetController::ReleaseReset(module, target_clock_frequency_khz); + } + + void SdmmcController::AssertReset() { + return ClockResetController::AssertReset(this->GetClockResetModule()); + } + + Result SdmmcController::StartupCore(BusPower bus_power) { + /* Set schmitt trigger. */ + this->SetSchmittTrigger(bus_power); + + /* Select one-cycle delay version of cmd_oen. */ + reg::ReadWrite(this->sdmmc_registers->io_spare, SD_REG_BITS_ENUM(IO_SPARE_SPARE_OUT_3, ONE_CYCLE_DELAY)); + + /* Select regulated reference voltage for trimmer and DLL supply. */ + reg::ReadWrite(this->sdmmc_registers->vendor_io_trim_cntrl, SD_REG_BITS_VALUE(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 0)); + + /* Configure outbound tap value. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TRIM_VAL, this->GetOutboundTapValue())); + + /* Configure SPI_MODE_CLKEN_OVERRIDE. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, NORMAL)); + + /* Set slew codes. */ + this->SetSlewCodes(); + + /* Set vref sel. */ + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, this->GetVrefSelValue())); + + /* Perform drive strength calibration at the new power. */ + this->SetDriveCodeOffsets(bus_power); + this->CalibrateDriveStrength(bus_power); + + /* Enable internal clock. */ + R_TRY(SdHostStandardController::EnableInternalClock()); + + return ResultSuccess(); + } + + Result SdmmcController::SetClockTrimmer(SpeedMode speed_mode, u8 tap_value) { + /* If speed mode is Hs400, set the dqs trim value. */ + if (speed_mode == SpeedMode_MmcHs400) { + reg::ReadWrite(this->sdmmc_registers->vendor_cap_overrides, SD_REG_BITS_VALUE(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 40)); + } + + /* Configure tap value as updated by software. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, NOT_UPDATED_BY_HW)); + + /* Set the inbound tap value. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TAP_VAL, tap_value)); + + /* Reset the cmd/dat line. */ + R_TRY(SdHostStandardController::ResetCmdDatLine()); + + return ResultSuccess(); + } + + u8 SdmmcController::GetCurrentTapValue() { + return static_cast(reg::GetValue(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_MASK(VENDOR_CLOCK_CNTRL_TAP_VAL))); + } + + Result SdmmcController::CalibrateDll() { + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Begin calibration. */ + reg::ReadWrite(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE)); + + /* Wait up to 5ms for calibration to begin. */ + { + ManualTimer timer(5); + while (true) { + /* If calibration is done, we're done. */ + if (!reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE))) { + break; + } + + /* Otherwise, check if we've timed out. */ + R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllCalibrationSoftwareTimeout()); + } + } + + /* Wait up to 10ms for calibration to complete. */ + { + ManualTimer timer(10); + while (true) { + /* If calibration is done, we're done. */ + if (reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg_sta, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, DONE))) { + break; + } + + /* Otherwise, check if we've timed out. */ + R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllApplicationSoftwareTimeout()); + } + } + + return ResultSuccess(); + } + + Result SdmmcController::SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value) { + /* Check if we need to temporarily disable the device clock. */ + const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that the clock is disabled for the period we're using it. */ + if (clock_enabled) { + /* Turn off the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + /* Set clock trimmer. */ + /* NOTE: Nintendo does not re-enable the clock if this fails... */ + R_TRY(this->SetClockTrimmer(speed_mode, tap_value)); + + /* Configure for the desired speed mode. */ + switch (speed_mode) { + case SpeedMode_MmcIdentification: + case SpeedMode_SdCardIdentification: + case SpeedMode_MmcLegacySpeed: + case SpeedMode_SdCardDefaultSpeed: + /* Set as normal speed, 3.3V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, NORMAL_SPEED)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); + break; + case SpeedMode_MmcHighSpeed: + case SpeedMode_SdCardHighSpeed: + /* Set as high speed, 3.3V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, HIGH_SPEED)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); + break; + case SpeedMode_MmcHs200: + /* Set as HS200, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS200)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_MmcHs400: + /* Set as HS400, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS400)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_SdCardSdr12: + /* Set as SDR12, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR12)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_SdCardSdr50: + case SpeedMode_SdCardSdr104: + case SpeedMode_GcAsicFpgaSpeed: + case SpeedMode_GcAsicSpeed: + /* Set as SDR104, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR104)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + SdHostStandardController::EnsureControl(); + + /* Get the divider setting. */ + u32 target_source_clock_frequency_khz; + u16 x; + GetDividerSetting(std::addressof(target_source_clock_frequency_khz), std::addressof(x), speed_mode); + + /* Set the clock frequency. */ + u32 actual_source_clock_frequency_khz; + ClockResetController::SetClockFrequencyKHz(std::addressof(actual_source_clock_frequency_khz), this->GetClockResetModule(), target_source_clock_frequency_khz); + + /* Set the device clock frequency. */ + const u32 actual_device_clock_frequency_khz = util::DivideUp(actual_source_clock_frequency_khz, x); + SdHostStandardController::SetDeviceClockFrequencyKHz(actual_device_clock_frequency_khz); + + /* Check that the divider is correct. */ + AMS_ABORT_UNLESS((x == 1) || util::IsAligned(x, 2)); + + /* Write the divider val to clock control. */ + const u16 n = x / 2; + const u16 upper_n = n >> 8; + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_VALUE(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, n), + SD_REG_BITS_VALUE(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, upper_n)); + + /* Re-enable the clock, if we should. */ + if (clock_enabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + + /* If speed mode is Hs400, calibrate dll. */ + if (speed_mode == SpeedMode_MmcHs400) { + R_TRY(this->CalibrateDll()); + } + + /* Set the current speed mode. */ + this->current_speed_mode = speed_mode; + + return ResultSuccess(); + } + + Result SdmmcController::IssueTuningCommand(u32 command_index) { + /* Check that we're not power saving enable. */ + AMS_ABORT_UNLESS(!SdHostStandardController::IsPowerSavingEnable()); + + /* Wait until command inhibit is done. */ + R_TRY(SdHostStandardController::WaitWhileCommandInhibit(true)); + + /* Set transfer for tuning. */ + SdHostStandardController::SetTransferForTuning(); + + /* If necessary, clear interrupt and enable buffer read ready signal. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + this->ClearInterrupt(); + + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + } + #endif + + /* Set the buffer read ready enable, and read status to ensure it takes. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, reg::Read(this->sdmmc_registers->sd_host_standard_registers.normal_int_status)); + + /* Issue command with clock disabled. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + { + SdHostStandardController::SetCommandForTuning(command_index); + + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1); + SdHostStandardController::AbortTransaction(); + } + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* When we're done waiting, ensure that we clean up appropriately. */ + ON_SCOPE_EXIT { + /* Clear the buffer read ready signal, if we should. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); + #endif + + /* Clear the buffer read ready enable. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); + + /* Wait 8 clocks to ensure configuration takes. */ + SdHostStandardController::EnsureControl(); + WaitClocks(8, SdHostStandardController::GetDeviceClockFrequencyKHz()); + }; + + /* Wait for the command to finish. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + const auto result = SdHostStandardController::WaitInterrupt(TuningCommandTimeoutMilliSeconds); + if (R_SUCCEEDED(result)) { + /* If we succeeded, clear the interrupt. */ + reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + this->ClearInterrupt(); + return ResultSuccess(); + } else if (sdmmc::ResultWaitInterruptSoftwareTimeout::Includes(result)) { + SdHostStandardController::AbortTransaction(); + return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); + } else { + return result; + } + } + #else + { + SdHostStandardController::EnsureControl(); + ManualTimer timer(TuningCommandTimeoutMilliSeconds); + while (true) { + /* Check if we received the interrupt. */ + if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED))) { + /* If we did, acknowledge it. */ + reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + return ResultSuccess(); + } + + /* Otherwise, check if we timed out. */ + if (!timer.Update()) { + SdHostStandardController::AbortTransaction(); + return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); + } + } + } + #endif + } + + void SdmmcController::SetDriveCodeOffsets(BusPower bus_power) { + /* Get the offsets. */ + u8 pd, pu; + this->GetAutoCalOffsets(std::addressof(pd), std::addressof(pu), bus_power); + + /* Set the offsets. */ + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, pd), + SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, pu)); + + /* Wait for 1ms to ensure that our configuration takes. */ + /* NOTE/HACK: Nintendo does not (need) to do this, but they use SvcSleepThread for waits. */ + /* It's unclear why this wait is necessary, but not doing it causes drive strength calibration to fail if done immediately afterwards. */ + util::WaitMicroSeconds(1000); + } + + void SdmmcController::CalibrateDriveStrength(BusPower bus_power) { + /* Reset drive strength calibration status. */ + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); + + /* Check if we need to temporarily disable the device clock. */ + const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that the clock is disabled for the period we're using it. */ + if (clock_enabled) { + /* Turn off the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + /* Calibrate with the clock disabled. */ + { + /* Set SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ + if (reg::HasValue(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0))) { + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 1)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1); + } + + /* Calibrate with SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD set. */ + { + /* Begin autocal. */ + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, ENABLED), + SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, ENABLED)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(2); + + /* Wait up to 10ms for auto cal to complete. */ + ManualTimer timer(10); + while (true) { + /* Check if auto cal is inactive. */ + if (reg::HasValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, INACTIVE))) { + /* Check the pullup status. */ + const u32 pullup = (reg::GetValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_MASK(AUTO_CAL_STATUS_AUTO_CAL_PULLUP))) & 0x1F; + if (pullup == 0x1F) { + this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompShortToGnd(); + } + if (pullup == 0) { + this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompOpen(); + } + + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationSoftwareTimeout(); + + this->SetDriveStrengthToDefaultValues(bus_power); + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, DISABLED)); + break; + } + } + } + + /* Clear SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0)); + } + + /* Re-enable the clock, if we should. */ + if (clock_enabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + + /* If calibration didn't receive a replacement error, set internal state to success. */ + if (sdmmc::ResultDriveStrengthCalibrationNotCompleted::Includes(this->drive_strength_calibration_status)) { + this->drive_strength_calibration_status = ResultSuccess(); + } + } + + Result SdmmcController::Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) { + /* Verify that we're awake. */ + AMS_ABORT_UNLESS(this->is_awake); + + /* Release the controller from reset. */ + this->ReleaseReset(speed_mode); + + /* Mark that we're not shutdown. */ + this->is_shutdown = false; + + /* Power on the controller. */ + R_TRY(this->PowerOn(bus_power)); + + /* Start up for the specific power. */ + R_TRY(this->StartupCore(bus_power)); + + /* Set our current power/width/speed. */ + SdHostStandardController::SetBusWidth(bus_width); + SdHostStandardController::SetBusPower(bus_power); + R_TRY(this->SetSpeedMode(speed_mode)); + this->SetPowerSaving(power_saving_enable); + + /* Enable clock to the device. */ + SdHostStandardController::EnableDeviceClock(); + + /* Ensure that we can control the device. */ + SdHostStandardController::EnsureControl(); + + return ResultSuccess(); + } + + void SdmmcController::Shutdown() { + /* If we're already shut down, there's nothing to do. */ + if (this->is_shutdown) { + return; + } + + /* If we're currently awake, we need to disable clock/power. */ + if (this->is_awake) { + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::SetBusPower(BusPower_Off); + SdHostStandardController::EnsureControl(); + } + + /* Power off. */ + this->PowerOff(); + + /* If awake, assert reset. */ + if (this->is_awake) { + this->AssertReset(); + } + + /* Mark that we're shutdown. */ + this->is_shutdown = true; + } + + void SdmmcController::PutToSleep() { + /* If we're already shut down or asleep, there's nothing to do. */ + if (this->is_shutdown || !this->is_awake) { + return; + } + + /* Save values before sleep. */ + this->bus_power_before_sleep = SdHostStandardController::GetBusPower(); + this->bus_width_before_sleep = SdHostStandardController::GetBusWidth(); + this->speed_mode_before_sleep = this->current_speed_mode; + this->tap_value_before_sleep = this->GetCurrentTapValue(); + this->is_powersaving_enable_before_sleep = SdHostStandardController::IsPowerSavingEnable(); + + /* Disable clock/power to the device. */ + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::SetBusPower(BusPower_Off); + SdHostStandardController::EnsureControl(); + + /* Assert reset. */ + this->AssertReset(); + + /* Mark that we're asleep. */ + this->is_awake = false; + } + + Result SdmmcController::Awaken() { + /* If we're shut down, or if we're awake already, there's nothing to do. */ + R_SUCCEED_IF(this->is_shutdown); + R_SUCCEED_IF(this->is_awake); + + /* Mark that we're awake. */ + this->is_awake = true; + + /* Clear pad parked status. */ + this->ClearPadParked(); + + /* Release reset. */ + this->ReleaseReset(this->speed_mode_before_sleep); + + /* Start up for the correct power. */ + R_TRY(this->StartupCore(this->bus_power_before_sleep)); + + /* Configure values to what they were before sleep. */ + SdHostStandardController::SetBusWidth(this->bus_width_before_sleep); + SdHostStandardController::SetBusPower(this->bus_power_before_sleep); + R_TRY(this->SetSpeedModeWithTapValue(this->speed_mode_before_sleep, this->tap_value_before_sleep)); + this->SetPowerSaving(this->is_powersaving_enable_before_sleep); + + /* Enable clock to the device. */ + SdHostStandardController::EnableDeviceClock(); + SdHostStandardController::EnsureControl(); + + return ResultSuccess(); + } + + Result SdmmcController::SwitchToSdr12() { + /* Disable clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Check that the dat lines are all low. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b0000)), sdmmc::ResultSdCardNotReadyToVoltageSwitch()); + + /* Set Speed Mode. */ + R_TRY(this->SetSpeedMode(SpeedMode_SdCardSdr12)); + + /* Set voltage to 1.8V. */ + SdHostStandardController::EnsureControl(); + R_TRY(this->LowerBusPower()); + this->SetSchmittTrigger(BusPower_1_8V); + + /* Perform drive strength calibration at the new power. */ + this->SetDriveCodeOffsets(BusPower_1_8V); + this->CalibrateDriveStrength(BusPower_1_8V); + + /* Set the bus power in standard controller. */ + SdHostStandardController::SetBusPower(BusPower_1_8V); + + /* Wait up to 5ms for the switch to take. */ + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(5000); + + /* Check that we switched to 1.8V. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)), sdmmc::ResultSdHostStandardFailSwitchTo1_8V()); + + /* Enable clock, and wait 1ms. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1000); + + /* Check that the dat lines are all high. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b1111)), sdmmc::ResultSdCardNotCompleteVoltageSwitch()); + + return ResultSuccess(); + } + + Result SdmmcController::SetSpeedMode(SpeedMode speed_mode) { + /* Get the tap value. */ + u8 tap_value; + if (speed_mode == SpeedMode_MmcHs400) { + AMS_ABORT_UNLESS(this->is_valid_tap_value_for_hs_400); + tap_value = this->tap_value_for_hs_400; + } else { + tap_value = this->GetDefaultInboundTapValue(); + } + + /* Set the speed mode. */ + R_TRY(this->SetSpeedModeWithTapValue(speed_mode, tap_value)); + + return ResultSuccess(); + } + + void SdmmcController::SetPowerSaving(bool en) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && !en && SdHostStandardController::IsDeviceClockEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::SetPowerSaving(en); + } + + void SdmmcController::EnableDeviceClock() { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && !SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::EnableDeviceClock(); + } + + Result SdmmcController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::IssueCommand(command, xfer_data, out_num_transferred_blocks); + } + + Result SdmmcController::IssueStopTransmissionCommand(u32 *out_response) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::IssueStopTransmissionCommand(out_response); + } + + Result SdmmcController::Tuning(SpeedMode speed_mode, u32 command_index) { + /* Clear vendor tuning control 1. */ + reg::Write(this->sdmmc_registers->vendor_tuning_cntrl1, 0); + + /* Determine/configure the number of tries. */ + int num_tries; + switch (speed_mode) { + case SpeedMode_MmcHs200: + case SpeedMode_MmcHs400: + case SpeedMode_SdCardSdr104: + num_tries = 128; + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_128)); + break; + case SpeedMode_SdCardSdr50: + case SpeedMode_GcAsicFpgaSpeed: + case SpeedMode_GcAsicSpeed: + num_tries = 256; + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_256)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Configure the multiplier. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_VALUE(VENDOR_TUNING_CNTRL0_MUL_M, 1)); + + /* Configure tap value to be updated by hardware. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, UPDATED_BY_HW)); + + /* Configure to execute tuning. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, EXECUTE_TUNING)); + + /* Perform tuning num_tries times. */ + for (int i = 0; /* ... */; ++i) { + /* Check if we've been removed. */ + R_TRY(this->CheckRemoved()); + + /* Issue the command. */ + this->IssueTuningCommand(command_index); + + /* Check if tuning is done. */ + if (i >= num_tries) { + break; + } + + if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, TUNING_COMPLETED))) { + break; + } + } + + /* Check if we're using the tuned clock. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, USING_TUNED_CLOCK)), sdmmc::ResultTuningFailed()); + + return ResultSuccess(); + } + + void SdmmcController::SaveTuningStatusForHs400() { + /* Save the current tap value. */ + this->tap_value_for_hs_400 = GetCurrentTapValue(); + this->is_valid_tap_value_for_hs_400 = true; + } + + Result Sdmmc1Controller::PowerOnForRegisterControl(BusPower bus_power) { + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; + + /* pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ + R_TRY(this->power_controller->PowerOn(BusPower_3_3V)); + + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerOffForRegisterControl() { + /* If we're already off, there's nothing to do. */ + if (this->current_bus_power == BusPower_Off) { + return; + } + + /* If we're at 3.3V, lower to 1.8V. */ + if (this->current_bus_power == BusPower_3_3V) { + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + this->power_controller->LowerBusPower(); + + /* Set our bus power. */ + this->current_bus_power = BusPower_1_8V; + } + + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1OutputHigh); + + + /* pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + this->power_controller->PowerOff(); + + /* Set our bus power. */ + this->current_bus_power = BusPower_Off; + + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1ResetState); + } + + Result Sdmmc1Controller::LowerBusPowerForRegisterControl() { + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; + + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + R_TRY(this->power_controller->LowerBusPower()); + + return ResultSuccess(); + } + + void Sdmmc1Controller::SetSchmittTriggerForRegisterControl(BusPower bus_power) { + SdHostStandardController::EnsureControl(); + + if (IsSocMariko()) { + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); + } else { + switch (bus_power) { + case BusPower_1_8V: + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); + break; + case BusPower_3_3V: + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtDisable); + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + Result Sdmmc1Controller::PowerOnForPcvControl(BusPower bus_power) { + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; + + /* TODO: return pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerOffForPcvControl() { + /* If we're already off, there's nothing to do. */ + if (this->current_bus_power == BusPower_Off) { + return; + } + + /* If we're at 3.3V, lower to 1.8V. */ + { + /* TODO: pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + this->current_bus_power = BusPower_1_8V; + } + + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + + /* TODO: pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + this->current_bus_power = BusPower_Off; + + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + + } + + Result Sdmmc1Controller::LowerBusPowerForPcvControl() { + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; + + /* TODO: return pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + return ResultSuccess(); + } + + void Sdmmc1Controller::SetSchmittTriggerForPcvControl(BusPower bus_power) { + SdHostStandardController::EnsureControl(); + + if (IsSocMariko()) { + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + } else { + switch (bus_power) { + case BusPower_1_8V: + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + break; + case BusPower_3_3V: + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + #endif + + Result Sdmmc1Controller::PowerOn(BusPower bus_power) { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->PowerOnForPcvControl(bus_power); + } else + #endif + { + return this->PowerOnForRegisterControl(bus_power); + } + } + + void Sdmmc1Controller::PowerOff() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->PowerOffForPcvControl(); + } else + #endif + { + return this->PowerOffForRegisterControl(); + } + } + + Result Sdmmc1Controller::LowerBusPower() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->LowerBusPowerForPcvControl(); + } else + #endif + { + return this->LowerBusPowerForRegisterControl(); + } + } + + void Sdmmc1Controller::SetSchmittTrigger(BusPower bus_power) { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->SetSchmittTriggerForPcvControl(bus_power); + } else + #endif + { + return this->SetSchmittTriggerForRegisterControl(bus_power); + } + } + + void Sdmmc1Controller::Initialize() { + return this->InitializeForRegisterControl(); + } + + void Sdmmc1Controller::Finalize() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->FinalizeForPcvControl(); + } else + #endif + { + return this->FinalizeForRegisterControl(); + } + } + + void Sdmmc1Controller::InitializeForRegisterControl() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + #endif + + /* pinmux::Initialize(); */ + /* This just opens a session handle to pinmux service, no work to do. */ + + /* pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ + /* This just sets the session's internal value to the pin group name, so nothing to do here either. */ + + /* pcv::Initialize(); */ + /* This initializes a lot of globals in pcv, most of which we don't care about. */ + /* However, we do care about the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller == nullptr); + this->power_controller = new (GetPointer(this->power_controller_storage)) PowerController; + + /* Perform base initialization. */ + SdmmcController::Initialize(); + } + + void Sdmmc1Controller::FinalizeForRegisterControl() { + /* Perform base finalization. */ + SdmmcController::Finalize(); + + /* pcv::Finalize(); */ + /* As with initialize, we mostly don't care about the globals this touches. */ + /* However, we do want to finalize the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller != nullptr); + this->power_controller->~PowerController(); + this->power_controller = nullptr; + + /* pinmux::CloseSession(std::addressof(this->pinmux_session)); */ + /* This does nothing. */ + + /* pinmux::Finalize(); */ + /* This does nothing. */ + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + #endif + } + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void Sdmmc1Controller::InitializeForPcvControl() { + /* Mark ourselves as initialized by pcv control. */ + this->is_pcv_control = true; + + /* TODO: pinmux::Initialize(); */ + /* TODO: pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ + /* TODO: pcv::Initialize(); */ + + /* Perform base initialization. */ + SdmmcController::Initialize(); + } + + void Sdmmc1Controller::FinalizeForPcvControl() { + /* Perform base finalization. */ + SdmmcController::Finalize(); + + /* TODO: pcv::Finalize(); */ + /* TODO: pinmux::CloseSession(std::addressof(this->pinmux_session)); */ + /* TODO: pinmux::Finalize(); */ + + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + } + #endif + + namespace { + + constexpr inline dd::PhysicalAddress PmcRegistersPhysicalAddress = 0x7000E400; + + } + + Result Sdmmc1Controller::PowerController::ControlVddioSdmmc1(BusPower bus_power) { + /* Configure appropriate voltage. */ + switch (bus_power) { + case BusPower_Off: + R_TRY(SetSdCardVoltageEnabled(false)); + break; + case BusPower_1_8V: + R_TRY(SetSdCardVoltageValue(1'800'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + case BusPower_3_3V: + R_TRY(SetSdCardVoltageValue(3'300'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerController::SetSdmmcIoMode(bool is_3_3V) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcPwrDetValAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_PWR_DET_VAL; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(PWR_DET_VAL_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(PWR_DET_VAL_SDMMC1, is_3_3V, ENABLE, DISABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcPwrDetValAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + } + + void Sdmmc1Controller::PowerController::ControlRailSdmmc1Io(bool is_power_on) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcNoIoPowerAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_NO_IOPOWER; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(NO_IOPOWER_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(NO_IOPOWER_SDMMC1, is_power_on, DISABLE, ENABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcNoIoPowerAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + } + + Sdmmc1Controller::PowerController::PowerController() : current_bus_power(BusPower_Off) { + /* gpio::Initialize(); */ + /* ... */ + + /* Open gpio session. */ + /* gpio::OpenSession(std::addressof(this->gpio_pad_session), gpio::GpioPadName_PowSdEn); */ + gpio_impl::OpenSession(gpio_impl::GpioPadName_PowSdEn); + + /* Configure the gpio as low/output. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Output); */ + gpio_impl::SetDirection(gpio_impl::GpioPadName_PowSdEn, gpio_impl::Direction_Output); + } + + Sdmmc1Controller::PowerController::~PowerController() { + /* gpio::CloseSession(std::addressof(this->gpio_pad_session)); */ + gpio_impl::CloseSession(gpio_impl::GpioPadName_PowSdEn); + + /* gpio::Finalize(); */ + /* ... */ + } + + Result Sdmmc1Controller::PowerController::PowerOn(BusPower bus_power) { + /* Bus power should be off, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_Off); + R_SUCCEED_IF(this->current_bus_power != BusPower_Off); + + /* Power on requires the target bus power be 3.3V. */ + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Enable the rail. */ + this->ControlRailSdmmc1Io(true); + + /* Set the SD power GPIO to high. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_High); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_High); + + /* Wait 10ms for power change to take. */ + WaitMicroSeconds(10000); + + /* Configure Sdmmc1 IO as 3.3V. */ + this->SetSdmmcIoMode(true); + R_TRY(this->ControlVddioSdmmc1(BusPower_3_3V)); + + /* Wait 130 us for changes to take. */ + WaitMicroSeconds(130); + + /* Update our current bus power. */ + this->current_bus_power = bus_power; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::PowerOff() { + /* Bus power should be on, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power != BusPower_Off); + R_SUCCEED_IF(this->current_bus_power == BusPower_Off); + + /* Bus power should be 1.8V. */ + /* NOTE: the result returned here is 0x8C0 (regulator::ResultIllegalRequest()) on newer firmwares. */ + AMS_ASSERT(this->current_bus_power == BusPower_1_8V); + R_UNLESS(this->current_bus_power == BusPower_1_8V, pcv::ResultIllegalRequest()); + + /* Disable vddio, and wait 4 ms. */ + this->ControlVddioSdmmc1(BusPower_Off); + WaitMicroSeconds(4000); + + /* Set the SD power GPIO to low. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* Wait 239ms for the gpio config to take. */ + WaitMicroSeconds(239000); + + /* Disable the rail. */ + this->ControlRailSdmmc1Io(false); + this->SetSdmmcIoMode(true); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_Off; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::LowerBusPower() { + /* Bus power should be 3.3V, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_3_3V); + R_SUCCEED_IF(this->current_bus_power != BusPower_3_3V); + + /* Configure as 1.8V, then wait 150us for it to take. */ + R_TRY(this->ControlVddioSdmmc1(BusPower_1_8V)); + WaitMicroSeconds(150); + this->SetSdmmcIoMode(false); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_1_8V; + + return ResultSuccess(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp new file mode 100644 index 0000000..d007d9d --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp @@ -0,0 +1,682 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_sd_host_standard_controller.hpp" +#include "sdmmc_clock_reset_controller.hpp" + +namespace ams::sdmmc::impl { + + bool IsSocMariko(); + + constexpr inline size_t SdmmcRegistersSize = 0x200; + + constexpr inline dd::PhysicalAddress ApbMiscRegistersPhysicalAddress = UINT64_C(0x70000000); + constexpr inline size_t ApbMiscRegistersSize = 16_KB; + + class SdmmcController : public SdHostStandardController { + private: + struct SdmmcRegisters { + /* Standard registers. */ + volatile SdHostStandardRegisters sd_host_standard_registers; + + /* Vendor specific registers */ + volatile uint32_t vendor_clock_cntrl; + volatile uint32_t vendor_sys_sw_cntrl; + volatile uint32_t vendor_err_intr_status; + volatile uint32_t vendor_cap_overrides; + volatile uint32_t vendor_boot_cntrl; + volatile uint32_t vendor_boot_ack_timeout; + volatile uint32_t vendor_boot_dat_timeout; + volatile uint32_t vendor_debounce_count; + volatile uint32_t vendor_misc_cntrl; + volatile uint32_t max_current_override; + volatile uint32_t max_current_override_hi; + volatile uint32_t _0x12c[0x20]; + volatile uint32_t vendor_io_trim_cntrl; + + /* Start of sdmmc2/sdmmc4 only */ + volatile uint32_t vendor_dllcal_cfg; + volatile uint32_t vendor_dll_ctrl0; + volatile uint32_t vendor_dll_ctrl1; + volatile uint32_t vendor_dllcal_cfg_sta; + /* End of sdmmc2/sdmmc4 only */ + + volatile uint32_t vendor_tuning_cntrl0; + volatile uint32_t vendor_tuning_cntrl1; + volatile uint32_t vendor_tuning_status0; + volatile uint32_t vendor_tuning_status1; + volatile uint32_t vendor_clk_gate_hysteresis_count; + volatile uint32_t vendor_preset_val0; + volatile uint32_t vendor_preset_val1; + volatile uint32_t vendor_preset_val2; + volatile uint32_t sdmemcomppadctrl; + volatile uint32_t auto_cal_config; + volatile uint32_t auto_cal_interval; + volatile uint32_t auto_cal_status; + volatile uint32_t io_spare; + volatile uint32_t sdmmca_mccif_fifoctrl; + volatile uint32_t timeout_wcoal_sdmmca; + volatile uint32_t _0x1fc; + }; + static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize); + private: + SdmmcRegisters *sdmmc_registers; + bool is_shutdown; + bool is_awake; + SpeedMode current_speed_mode; + BusPower bus_power_before_sleep; + BusWidth bus_width_before_sleep; + SpeedMode speed_mode_before_sleep; + u8 tap_value_before_sleep; + bool is_powersaving_enable_before_sleep; + u8 tap_value_for_hs_400; + bool is_valid_tap_value_for_hs_400; + Result drive_strength_calibration_status; + private: + void ReleaseReset(SpeedMode speed_mode); + void AssertReset(); + Result StartupCore(BusPower bus_power); + Result SetClockTrimmer(SpeedMode speed_mode, u8 tap_value); + u8 GetCurrentTapValue(); + Result CalibrateDll(); + Result SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value); + Result IssueTuningCommand(u32 command_index); + protected: + void SetDriveCodeOffsets(BusPower bus_power); + void CalibrateDriveStrength(BusPower bus_power); + + virtual void SetPad() = 0; + + virtual ClockResetController::Module GetClockResetModule() const = 0; + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const = 0; + virtual os::InterruptEventType *GetInterruptEvent() const = 0; + #endif + + virtual bool IsNeedPeriodicDriveStrengthCalibration() = 0; + virtual void ClearPadParked() = 0; + virtual Result PowerOn(BusPower bus_power) = 0; + virtual void PowerOff() = 0; + virtual Result LowerBusPower() = 0; + virtual void SetSchmittTrigger(BusPower bus_power) = 0; + virtual u8 GetOutboundTapValue() const = 0; + virtual u8 GetDefaultInboundTapValue() const = 0; + virtual u8 GetVrefSelValue() const = 0; + virtual void SetSlewCodes() = 0; + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const = 0; + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) = 0; + public: + explicit SdmmcController(dd::PhysicalAddress registers_phys_addr) : SdHostStandardController(registers_phys_addr, SdmmcRegistersSize) { + /* Set sdmmc registers. */ + static_assert(offsetof(SdmmcRegisters, sd_host_standard_registers) == 0); + this->sdmmc_registers = reinterpret_cast(this->registers); + + this->is_shutdown = true; + this->is_awake = true; + this->is_valid_tap_value_for_hs_400 = false; + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); + this->tap_value_for_hs_400 = 0; + this->current_speed_mode = SpeedMode_MmcIdentification; + this->bus_power_before_sleep = BusPower_Off; + this->bus_width_before_sleep = BusWidth_1Bit; + this->speed_mode_before_sleep = SpeedMode_MmcIdentification; + this->tap_value_before_sleep = 0; + this->is_powersaving_enable_before_sleep = false; + } + + virtual void Initialize() override { + /* Set pad. */ + this->SetPad(); + + /* Initialize our clock/reset module. */ + ClockResetController::Initialize(this->GetClockResetModule()); + + /* If necessary, initialize our interrupt event. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::InterruptEventType *interrupt_event = this->GetInterruptEvent(); + os::InitializeInterruptEvent(interrupt_event, this->GetInterruptNumber(), os::EventClearMode_ManualClear); + SdHostStandardController::PreSetInterruptEvent(interrupt_event); + } + #endif + + /* Perform base initialization. */ + SdHostStandardController::Initialize(); + } + + virtual void Finalize() override { + /* Perform base finalization. */ + SdHostStandardController::Finalize(); + + /* If necessary, finalize our interrupt event. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::FinalizeInterruptEvent(this->GetInterruptEvent()); + } + #endif + + /* Finalize our clock/reset module. */ + ClockResetController::Finalize(this->GetClockResetModule()); + } + + virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) override; + virtual void Shutdown() override; + virtual void PutToSleep() override; + virtual Result Awaken() override; + virtual Result SwitchToSdr12() override; + virtual Result SetSpeedMode(SpeedMode speed_mode) override; + + virtual SpeedMode GetSpeedMode() const override { + return this->current_speed_mode; + } + + virtual void SetPowerSaving(bool en) override; + virtual void EnableDeviceClock() override; + + virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; + virtual Result IssueStopTransmissionCommand(u32 *out_response) override; + + virtual bool IsSupportedTuning() const override { + return true; + } + + virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override; + virtual void SaveTuningStatusForHs400() override; + + virtual Result GetInternalStatus() const override { + return this->drive_strength_calibration_status; + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc1RegistersPhysicalAddress = UINT64_C(0x700B0000); + + class Sdmmc1Controller : public SdmmcController { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + + /* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */ + class PowerController { + NON_COPYABLE(PowerController); + NON_MOVEABLE(PowerController); + private: + BusPower current_bus_power; + private: + Result ControlVddioSdmmc1(BusPower bus_power); + void SetSdmmcIoMode(bool is_3_3V); + void ControlRailSdmmc1Io(bool is_power_on); + public: + PowerController(); + ~PowerController(); + + Result PowerOn(BusPower bus_power); + Result PowerOff(); + Result LowerBusPower(); + }; + private: + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* TODO: pinmux::PinmuxSession pinmux_session; */ + #endif + BusPower current_bus_power; + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + bool is_pcv_control; + #endif + TYPED_STORAGE(PowerController) power_controller_storage; + PowerController *power_controller; + private: + Result PowerOnForRegisterControl(BusPower bus_power); + void PowerOffForRegisterControl(); + Result LowerBusPowerForRegisterControl(); + void SetSchmittTriggerForRegisterControl(BusPower bus_power); + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + Result PowerOnForPcvControl(BusPower bus_power); + void PowerOffForPcvControl(); + Result LowerBusPowerForPcvControl(); + void SetSchmittTriggerForPcvControl(BusPower bus_power); + #endif + protected: + virtual void SetPad() override { + /* Nothing is needed here. */ + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc1; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 46; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual bool IsNeedPeriodicDriveStrengthCalibration() override { + return !IsSocMariko(); + } + + virtual void ClearPadParked() override { + /* Nothing is needed here. */ + } + + virtual Result PowerOn(BusPower bus_power) override; + virtual void PowerOff() override; + virtual Result LowerBusPower() override; + + virtual void SetSchmittTrigger(BusPower bus_power) override; + + virtual u8 GetOutboundTapValue() const override { + if (IsSocMariko()) { + return 0xE; + } else { + return 0x2; + } + } + + virtual u8 GetDefaultInboundTapValue() const override { + if (IsSocMariko()) { + return 0xB; + } else { + return 0x4; + } + } + + virtual u8 GetVrefSelValue() const override { + if (IsSocMariko()) { + return 0x0; + } else { + return 0x7; + } + } + + virtual void SetSlewCodes() override { + if (IsSocMariko()) { + /* Do nothing. */ + } else { + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Write the slew values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 0x1), + APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 0x1)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); + } + } + + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { + /* Ensure that we can write the offsets. */ + AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); + AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); + + /* Set the offsets. */ + if (IsSocMariko()) { + switch (bus_power) { + case BusPower_1_8V: + *out_auto_cal_pd_offset = 6; + *out_auto_cal_pu_offset = 6; + break; + case BusPower_3_3V: + *out_auto_cal_pd_offset = 0; + *out_auto_cal_pu_offset = 0; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } else { + switch (bus_power) { + case BusPower_1_8V: + *out_auto_cal_pd_offset = 0x7B; + *out_auto_cal_pu_offset = 0x7B; + break; + case BusPower_3_3V: + *out_auto_cal_pd_offset = 0x7D; + *out_auto_cal_pu_offset = 0; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Determine the drive code values. */ + u8 drvdn, drvup; + if (IsSocMariko()) { + drvdn = 0x8; + drvup = 0x8; + } else { + switch (bus_power) { + case BusPower_1_8V: + drvdn = 0xF; + drvup = 0xB; + break; + case BusPower_3_3V: + drvdn = 0xC; + drvup = 0xC; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + /* Write the drv up/down values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, drvdn), + APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, drvup)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); + } + public: + Sdmmc1Controller() : SdmmcController(Sdmmc1RegistersPhysicalAddress) { + this->current_bus_power = BusPower_Off; + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + this->is_pcv_control = false; + #endif + this->power_controller = nullptr; + } + + virtual void Initialize() override; + virtual void Finalize() override; + + void InitializeForRegisterControl(); + void FinalizeForRegisterControl(); + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void InitializeForPcvControl(); + void FinalizeForPcvControl(); + #endif + + virtual bool IsSupportedBusPower(BusPower bus_power) const override { + switch (bus_power) { + case BusPower_Off: return true; + case BusPower_1_8V: return true; + case BusPower_3_3V: return true; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { + switch (bus_width) { + case BusWidth_1Bit: return true; + case BusWidth_4Bit: return true; + case BusWidth_8Bit: return false; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + }; + + class Sdmmc2And4Controller : public SdmmcController { + protected: + virtual bool IsNeedPeriodicDriveStrengthCalibration() override { + return false; + } + + virtual Result PowerOn(BusPower bus_power) override { + /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ + AMS_UNUSED(bus_power); + return ResultSuccess(); + } + + virtual void PowerOff() override { + /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ + } + + virtual Result LowerBusPower() override { + AMS_ABORT("Sdmmc2And4Controller cannot lower bus power\n"); + } + + virtual void SetSchmittTrigger(BusPower bus_power) override { + /* Do nothing. */ + AMS_UNUSED(bus_power); + } + + virtual u8 GetOutboundTapValue() const override { + if (IsSocMariko()) { + return 0xD; + } else { + return 0x8; + } + } + + virtual u8 GetDefaultInboundTapValue() const override { + if (IsSocMariko()) { + return 0xB; + } else { + return 0x0; + } + } + + virtual u8 GetVrefSelValue() const override { + return 0x7; + } + + virtual void SetSlewCodes() override { + /* Do nothing. */ + } + + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { + /* Ensure that we can write the offsets. */ + AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); + AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); + + /* Sdmmc2And4Controller only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Set the offsets. */ + *out_auto_cal_pd_offset = 5; + *out_auto_cal_pu_offset = 5; + } + public: + explicit Sdmmc2And4Controller(dd::PhysicalAddress registers_phys_addr) : SdmmcController(registers_phys_addr) { + /* ... */ + } + + virtual bool IsSupportedBusPower(BusPower bus_power) const override { + switch (bus_power) { + case BusPower_Off: return true; + case BusPower_1_8V: return true; + case BusPower_3_3V: return false; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { + switch (bus_width) { + case BusWidth_1Bit: return true; + case BusWidth_4Bit: return true; + case BusWidth_8Bit: return true; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc2RegistersPhysicalAddress = UINT64_C(0x700B0200); + + class Sdmmc2Controller : public Sdmmc2And4Controller { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + protected: + virtual void SetPad() override { + /* Nothing is needed here. */ + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc2; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 47; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual void ClearPadParked() override { + if (IsSocMariko()) { + /* Nothing is needed here. */ + } else { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Clear all MISC2PMC_EMMC2_*_PARK bits. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 0)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); + } + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* SDMMC2 only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + if (IsSocMariko()) { + /* Write the drv up/down values to APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 0xA), + APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 0xA)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL); + } else { + /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 0x10), + APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 0x10)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); + } + } + public: + Sdmmc2Controller() : Sdmmc2And4Controller(Sdmmc2RegistersPhysicalAddress) { + /* ... */ + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600); + + class Sdmmc4Controller : public Sdmmc2And4Controller { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + protected: + virtual void SetPad() override { + if (IsSocMariko()) { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Enable Schmitt Trigger in emmc4 iobrick. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, ENABLE)); + + /* Clear CMD_PULLU, CLK_PULLD, DQS_PULLD. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 0), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 0), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 0)); + + /* Read again to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL); + } else { + /* On Erista, we can just leave the reset value intact. */ + } + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc4; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 63; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual void ClearPadParked() override { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Clear all MISC2PMC_EMMC4_*_PARK bits. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 0)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* SDMMC4 only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Determine the drv up/down values. */ + u8 drvdn, drvup; + if (IsSocMariko()) { + drvdn = 0xA; + drvup = 0xA; + } else { + drvdn = 0x10; + drvup = 0x10; + } + + /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, drvdn), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, drvup)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); + } + public: + Sdmmc4Controller() : Sdmmc2And4Controller(Sdmmc4RegistersPhysicalAddress) { + /* ... */ + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_select_sdmmc_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_select_sdmmc_controller.hpp new file mode 100644 index 0000000..9d3b943 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_select_sdmmc_controller.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" + + namespace ams::sdmmc::impl { + + using SdmmcControllerForPortSdCard0 = Sdmmc1Controller; + using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller; + using SdmmcControllerForPortMmc0 = Sdmmc4Controller; + + } + +#else + #error "Unknown board for ams::sdmmc::SdmmcController" +#endif diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.cpp new file mode 100644 index 0000000..ae90332 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.cpp @@ -0,0 +1,90 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + namespace { + + #if defined(AMS_SDMMC_USE_OS_TIMER) + void SpinWaitMicroSeconds(u32 us) { + const os::Tick timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMicroSeconds(us)) + os::Tick(1); + while (true) { + if (os::GetSystemTick() > timeout_tick) { + break; + } + } + } + + ALWAYS_INLINE void DataSynchronizationBarrier() { + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("dsb sy" ::: "memory"); + #elif defined(ATMOSPHERE_ARCH_ARM) + __asm__ __volatile__("dsb" ::: "memory"); + #else + #error "Unknown architecture for DataSynchronizationBarrier" + #endif + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + #if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM) + __asm__ __volatile__("isb" ::: "memory"); + #else + #error "Unknown architecture for InstructionSynchronizationBarrier" + #endif + } + #endif + + } + + void WaitMicroSeconds(u32 us) { + #if defined(AMS_SDMMC_USE_OS_TIMER) + /* Ensure that nothing is reordered before we wait. */ + DataSynchronizationBarrier(); + InstructionSynchronizationBarrier(); + + /* If the time is small, spinloop, otherwise pend ourselves. */ + if (us < 100) { + SpinWaitMicroSeconds(us); + } else { + os::SleepThread(TimeSpan::FromMicroSeconds(us)); + } + + /* Ensure that nothing is reordered after we wait. */ + DataSynchronizationBarrier(); + InstructionSynchronizationBarrier(); + #elif defined(AMS_SDMMC_USE_UTIL_TIMER) + util::WaitMicroSeconds(us); + #else + #error "Unknown context for ams::sdmmc::impl::WaitMicroSeconds" + #endif + } + + void WaitClocks(u32 num_clocks, u32 clock_frequency_khz) { + AMS_ABORT_UNLESS(clock_frequency_khz > 0); + WaitMicroSeconds(util::DivideUp(1000 * num_clocks, clock_frequency_khz)); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.hpp new file mode 100644 index 0000000..5d81495 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.hpp @@ -0,0 +1,68 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + void WaitMicroSeconds(u32 us); + void WaitClocks(u32 num_clocks, u32 clock_frequency_khz); + + #if defined(AMS_SDMMC_USE_OS_TIMER) + class ManualTimer { + private: + os::Tick timeout_tick; + bool is_timed_out; + public: + explicit ManualTimer(u32 ms) { + this->timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(ms)); + this->is_timed_out = false; + } + + bool Update() { + if (this->is_timed_out) { + return false; + } + + this->is_timed_out = os::GetSystemTick() > this->timeout_tick; + return true; + } + }; + #elif defined(AMS_SDMMC_USE_UTIL_TIMER) + class ManualTimer { + private: + u32 timeout_us; + bool is_timed_out; + public: + explicit ManualTimer(u32 ms) { + this->timeout_us = util::GetMicroSeconds() + (ms * 1000); + this->is_timed_out = false; + } + + bool Update() { + if (this->is_timed_out) { + return false; + } + + this->is_timed_out = util::GetMicroSeconds() > this->timeout_us; + return true; + } + }; + #else + #error "Unknown context for ams::sdmmc::ManualTimer" + #endif + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_common.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_common.cpp new file mode 100644 index 0000000..598e048 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_common.cpp @@ -0,0 +1,145 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_i_host_controller.hpp" +#include "impl/sdmmc_i_device_accessor.hpp" +#include "impl/sdmmc_clock_reset_controller.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::IHostController *GetHostController(Port port) { + /* Get the controller. */ + impl::IHostController *host_controller = nullptr; + switch (port) { + case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break; + case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break; + case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(host_controller != nullptr); + return host_controller; + } + + impl::IDeviceAccessor *GetDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::IDeviceAccessor *device_accessor = nullptr; + switch (port) { + case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break; + case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break; + case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(device_accessor != nullptr); + return device_accessor; + } + + } + + + + void Initialize(Port port) { + return GetDeviceAccessor(port)->Initialize(); + } + + void Finalize(Port port) { + return GetDeviceAccessor(port)->Finalize(); + } + +#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void SwitchToPcvClockResetControl() { + return impl::ClockResetController::SwitchToPcvControl(); + } +#endif + +#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + return GetDeviceAccessor(port)->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } + + void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + return GetDeviceAccessor(port)->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } +#endif + + void ChangeCheckTransferInterval(Port port, u32 ms) { + return GetHostController(port)->ChangeCheckTransferInterval(ms); + } + void SetDefaultCheckTransferInterval(Port port) { + return GetHostController(port)->SetDefaultCheckTransferInterval(); + } + + Result Activate(Port port) { + return GetDeviceAccessor(port)->Activate(); + } + + void Deactivate(Port port) { + return GetDeviceAccessor(port)->Deactivate(); + } + + Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors) { + return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, dst, dst_size, true); + } + + Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size) { + return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, const_cast(src), src_size, false); + } + + Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + + Result GetDeviceSpeedMode(SpeedMode *out, Port port) { + return GetDeviceAccessor(port)->GetSpeedMode(out); + } + + Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port) { + return GetDeviceAccessor(port)->GetMemoryCapacity(out_num_sectors); + } + + Result GetDeviceStatus(u32 *out_device_status, Port port) { + return GetDeviceAccessor(port)->GetDeviceStatus(out_device_status); + } + + Result GetDeviceCid(void *out, size_t out_size, Port port) { + return GetDeviceAccessor(port)->GetCid(out, out_size); + } + + Result GetDeviceCsd(void *out, size_t out_size, Port port) { + return GetDeviceAccessor(port)->GetCsd(out, out_size); + } + + void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port) { + return GetDeviceAccessor(port)->GetAndClearErrorInfo(out_error_info, out_log_size, out_log_buffer, log_buffer_size); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_gc_asic.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_gc_asic.cpp new file mode 100644 index 0000000..2071fd3 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_gc_asic.cpp @@ -0,0 +1,85 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_gc_asic_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr; + switch (port) { + case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr); + return gc_asic_device_accessor; + } + + } + + void PutGcAsicToSleep(Port port) { + return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep(); + } + + Result AwakenGcAsic(Port port) { + return GetGcAsicDeviceAccessor(port)->AwakenGcAsic(); + } + + Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) { + return GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size); + } + + Result FinishGcAsicOperation(Port port) { + return GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation(); + } + + Result AbortGcAsicOperation(Port port) { + return GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation(); + } + + Result SleepGcAsic(Port port) { + return GetGcAsicDeviceAccessor(port)->SleepGcAsic(); + } + + Result UpdateGcAsicKey(Port port) { + return GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey(); + } + + void SignalGcRemovedEvent(Port port) { + return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent(); + } + + void ClearGcRemovedEvent(Port port) { + return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_mmc.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_mmc.cpp new file mode 100644 index 0000000..76fc571 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_mmc.cpp @@ -0,0 +1,81 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_mmc_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::MmcDeviceAccessor *GetMmcDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::MmcDeviceAccessor *mmc_device_accessor = nullptr; + switch (port) { + case Port_Mmc0: mmc_device_accessor = impl::GetMmcDeviceAccessorOfPortMmc0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(mmc_device_accessor != nullptr); + return mmc_device_accessor; + } + + } + + void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size) { + return GetMmcDeviceAccessor(port)->SetMmcWorkBuffer(buffer, buffer_size); + } + + void PutMmcToSleep(Port port) { + return GetMmcDeviceAccessor(port)->PutMmcToSleep(); + } + + void AwakenMmc(Port port) { + return GetMmcDeviceAccessor(port)->AwakenMmc(); + } + + Result SelectMmcPartition(Port port, MmcPartition mmc_partition) { + return GetMmcDeviceAccessor(port)->SelectMmcPartition(mmc_partition); + } + + Result EraseMmc(Port port) { + return GetMmcDeviceAccessor(port)->EraseMmc(); + } + + Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port) { + return GetMmcDeviceAccessor(port)->GetMmcBootPartitionCapacity(out_num_sectors); + } + + Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port) { + return GetMmcDeviceAccessor(port)->GetMmcExtendedCsd(out_buffer, buffer_size); + } + + Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetMmcDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + +} diff --git a/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_sd_card.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_sd_card.cpp new file mode 100644 index 0000000..c10a38d --- /dev/null +++ b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_sd_card.cpp @@ -0,0 +1,103 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_sd_card_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::SdCardDeviceAccessor *GetSdCardDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::SdCardDeviceAccessor *sd_card_device_accessor = nullptr; + switch (port) { + case Port_SdCard0: sd_card_device_accessor = impl::GetSdCardDeviceAccessorOfPortSdCard0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(sd_card_device_accessor != nullptr); + return sd_card_device_accessor; + } + + } + + void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size) { + return GetSdCardDeviceAccessor(port)->SetSdCardWorkBuffer(buffer, buffer_size); + } + + void PutSdCardToSleep(Port port) { + return GetSdCardDeviceAccessor(port)->PutSdCardToSleep(); + } + + void AwakenSdCard(Port port) { + return GetSdCardDeviceAccessor(port)->AwakenSdCard(); + } + + Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardProtectedAreaCapacity(out_num_sectors); + } + + Result GetSdCardScr(void *dst, size_t dst_size, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardScr(dst, dst_size); + } + + Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function) { + return GetSdCardDeviceAccessor(port)->GetSdCardSwitchFunctionStatus(dst, dst_size, switch_function); + } + + Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode) { + return GetSdCardDeviceAccessor(port)->GetSdCardCurrentConsumption(out_current_consumption, speed_mode); + } + + Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardSdStatus(dst, dst_size); + } + + Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetSdCardDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + + bool IsSdCardInserted(Port port) { + return GetSdCardDeviceAccessor(port)->IsSdCardInserted(); + } + + bool IsSdCardRemoved(Port port) { + return GetSdCardDeviceAccessor(port)->IsSdCardRemoved(); + } + + + void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg) { + return GetSdCardDeviceAccessor(port)->RegisterSdCardDetectionEventCallback(callback, arg); + } + + void UnregisterSdCardDetectionEventCallback(Port port) { + return GetSdCardDeviceAccessor(port)->UnregisterSdCardDetectionEventCallback(); + } + +} + diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp deleted file mode 100644 index c9234e9..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_base_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - #if defined(AMS_SDMMC_THREAD_SAFE) - - #define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(this->base_device->device_mutex) - - #else - - #define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() - - #endif - - void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const { - AMS_ABORT_UNLESS(out_c_size_mult != nullptr); - AMS_ABORT_UNLESS(out_read_bl_len != nullptr); - - /* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */ - *out_c_size_mult = static_cast((this->csd[2] >> 7) & 0x7); - *out_read_bl_len = static_cast((this->csd[4] >> 8) & 0xF); - } - - Result BaseDevice::SetLegacyMemoryCapacity() { - /* Get csize from the csd. */ - const u32 c_size = ((this->csd[3] >> 6) & 0x3FF) | ((this->csd[4] & 0x3) << 10); - - /* Get c_size_mult and read_bl_len. */ - u8 c_size_mult, read_bl_len; - this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len)); - - /* Validate the parameters. */ - R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue()); - - /* Set memory capacity. */ - this->memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9); - this->is_valid_memory_capacity = true; - - return ResultSuccess(); - } - - Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const { - /* Check if there are any errors at all. */ - R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0); - - /* Check errors individually. */ - #define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__()) - - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error); - - if (this->GetDeviceType() == DeviceType_Mmc) { - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError); - } - - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand); - AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange); - - #undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR - - return ResultSuccess(); - } - - DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const { - return static_cast((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift); - } - - Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const { - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(command_index, command_arg, CommandResponseType, is_busy); - R_TRY(this->host_controller->IssueCommand(std::addressof(command))); - - /* Get the response. */ - AMS_ABORT_UNLESS(out_response != nullptr); - this->host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType); - - /* Mask out the ignored status bits. */ - if (status_ignore_mask != 0) { - *out_response &= ~status_ignore_mask; - } - - /* Check the r1 response for errors. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - R_TRY(this->base_device->CheckDeviceStatus(*out_response)); - - /* Check the device state. */ - if (expected_state != DeviceState_Unknown) { - R_UNLESS(this->base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState()); - } - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::IssueCommandGoIdleState() const { - /* Issue the command. */ - Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false); - return this->host_controller->IssueCommand(std::addressof(command)); - } - - Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const { - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R2; - Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false); - R_TRY(this->host_controller->IssueCommand(std::addressof(command))); - - /* Copy the data out. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(u32))); - AMS_ABORT_UNLESS(dst_size >= DeviceCidSize); - this->host_controller->GetLastResponse(static_cast(dst), DeviceCidSize, CommandResponseType); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::IssueCommandSelectCard() const { - /* Get the command argument. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - const u32 arg = static_cast(this->base_device->GetRca()) << 16; - - /* Issue the command. */ - return this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown); - } - - Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const { - /* Get the command argument. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - const u32 arg = static_cast(this->base_device->GetRca()) << 16; - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R2; - Command command(CommandIndex_SendCsd, arg, CommandResponseType, false); - R_TRY(this->host_controller->IssueCommand(std::addressof(command))); - - /* Copy the data out. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(u32))); - AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize); - this->host_controller->GetLastResponse(static_cast(dst), DeviceCsdSize, CommandResponseType); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const { - /* Get the command argument. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - const u32 arg = static_cast(this->base_device->GetRca()) << 16; - - /* Issue the command. */ - return this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask); - } - - Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const { - /* Issue the command. */ - return this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran); - } - - Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const { - /* Get the argument. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - const u32 arg = this->base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize; - - /* Get the command index and transfer direction. */ - const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock; - const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice; - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(command_index, arg, CommandResponseType, false); - TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true); - Result result = this->host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks); - - /* Handle the failure case. */ - if (R_FAILED(result)) { - /* Check if we were removed. */ - R_TRY(this->CheckRemoved()); - - /* By default, we'll want to return the result we just got. */ - Result result_to_return = result; - - /* Stop transmission. */ - u32 resp = 0; - result = this->host_controller->IssueStopTransmissionCommand(std::addressof(resp)); - if (R_SUCCEEDED(result)) { - result = this->base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand)); - if (R_FAILED(result)) { - result_to_return = result; - } - } - - /* Check if we were removed. */ - R_TRY(this->CheckRemoved()); - - /* Get the device status. */ - u32 device_status = 0; - result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand); - - /* If there's a device status error we don't already have, we prefer to return it. */ - if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) { - result_to_return = result; - } - - /* Return the result we chose. */ - return result_to_return; - } - - /* Get the responses. */ - u32 resp, st_resp; - this->host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - this->host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp)); - - /* Check the device status. */ - R_TRY(this->base_device->CheckDeviceStatus(resp)); - - /* Decide on what errors to ignore. */ - u32 status_ignore_mask = 0; - if (is_read) { - AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr); - if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) { - status_ignore_mask = DeviceStatus_AddressOutOfRange; - } - } - - /* Check the device status. */ - R_TRY(this->base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask)); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const { - /* Issue the read/write command. */ - AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr); - R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read)); - - /* Decide on what errors to ignore. */ - u32 status_ignore_mask = 0; - if (is_read) { - AMS_ABORT_UNLESS(this->base_device != nullptr); - if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) { - status_ignore_mask = DeviceStatus_AddressOutOfRange; - } - } - - /* Get and check the status. */ - u32 device_status; - R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask)); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) { - /* Verify that we can send the command. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - - /* If we want to read zero sectors, there's no work for us to do. */ - R_SUCCEED_IF(num_sectors == 0); - - /* Check that the buffer is big enough for the sectors we're reading. */ - AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors); - - /* Read sectors repeatedly until we've read all the ones we want. */ - u32 cur_sector_index = sector_index; - u32 remaining_sectors = num_sectors; - u8 *cur_buf = static_cast(buf); - while (remaining_sectors > 0) { - /* Determine how many sectors we can read in this iteration. */ - u32 cur_sectors = remaining_sectors; - if (sector_index_alignment > 0) { - AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0); - - const u32 max_sectors = this->host_controller->GetMaxTransferNumBlocks(); - if (remaining_sectors > max_sectors) { - cur_sectors = max_sectors - (max_sectors % sector_index_alignment); - } - } - - /* Try to perform the read/write. */ - u32 num_transferred_blocks = 0; - Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); - if (R_FAILED(result)) { - /* Check if we were removed. */ - R_TRY(this->CheckRemoved()); - - /* Log that we failed to read/write. */ - this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); - - /* Retry the read/write. */ - num_transferred_blocks = 0; - result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); - if (R_FAILED(result)) { - /* Check if we were removed. */ - R_TRY(this->CheckRemoved()); - - /* Log that we failed to read/write. */ - this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); - - /* Re-startup the connection, to see if that helps. */ - R_TRY(this->ReStartup()); - - /* Retry the read/write a third time. */ - num_transferred_blocks = 0; - result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read); - if (R_FAILED(result)) { - /* Log that we failed after a re-startup. */ - this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue()); - return result; - } - - /* Log that we succeeded after a retry. */ - this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors); - - /* Increment the number of error corrections we've done. */ - ++this->num_read_write_error_corrections; - } - } - - /* Update our tracking variables. */ - AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks); - remaining_sectors -= num_transferred_blocks; - cur_sector_index += num_transferred_blocks; - cur_buf += num_transferred_blocks * SectorSize; - } - - return ResultSuccess(); - } - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Register the address. */ - return this->host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); - } - - void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Register the address. */ - return this->host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); - } - #endif - - Result BaseDeviceAccessor::Activate() { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is awake. */ - R_UNLESS(this->base_device->IsAwake(), sdmmc::ResultNotAwakened()); - - /* If the device is already active, we don't need to do anything. */ - R_SUCCEED_IF(this->base_device->IsActive()); - - /* Activate the base device. */ - auto activate_guard = SCOPE_GUARD { ++this->num_activation_failures; }; - R_TRY(this->OnActivate()); - - /* We successfully activated the device. */ - activate_guard.Cancel(); - this->base_device->SetActive(); - - return ResultSuccess(); - } - - void BaseDeviceAccessor::Deactivate() { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Deactivate the base device. */ - if (this->base_device->IsActive()) { - this->host_controller->Shutdown(); - this->base_device->Deactivate(); - } - } - - Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Perform the read/write. */ - auto rw_guard = SCOPE_GUARD { ++this->num_read_write_failures; }; - R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read)); - - /* We successfully performed the read/write. */ - rw_guard.Cancel(); - return ResultSuccess(); - } - - Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the current speed mode/bus width. */ - *out_speed_mode = this->host_controller->GetSpeedMode(); - *out_bus_width = this->host_controller->GetBusWidth(); - - /* Verify that we can get the status. */ - R_TRY(this->host_controller->GetInternalStatus()); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the capacity. */ - AMS_ABORT_UNLESS(out_sectors != nullptr); - *out_sectors = this->base_device->GetMemoryCapacity(); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the status. */ - R_TRY(this->IssueCommandSendStatus(out, 0)); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetOcr(u32 *out) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the ocr. */ - AMS_ABORT_UNLESS(out != nullptr); - *out = this->base_device->GetOcr(); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetRca(u16 *out) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the rca. */ - AMS_ABORT_UNLESS(out != nullptr); - *out = this->base_device->GetRca(); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetCid(void *out, size_t size) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the cid. */ - this->base_device->GetCid(out, size); - - return ResultSuccess(); - } - - Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Check that the device is accessible. */ - R_TRY(this->base_device->CheckAccessible()); - - /* Get the csd. */ - this->base_device->GetCsd(out, size); - - return ResultSuccess(); - } - - void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) { - /* Lock exclusive access of the base device. */ - AMS_ABORT_UNLESS(this->base_device != nullptr); - AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX(); - - /* Set the output error info. */ - AMS_ABORT_UNLESS(out_error_info != nullptr); - out_error_info->num_activation_failures = this->num_activation_failures; - out_error_info->num_activation_error_corrections = this->num_activation_error_corrections; - out_error_info->num_read_write_failures = this->num_read_write_failures; - out_error_info->num_read_write_error_corrections = this->num_read_write_error_corrections; - this->ClearErrorInfo(); - - /* Check if we should write logs. */ - if (out_log_size == nullptr) { - return; - } - - /* Check if we can write logs. */ - if (out_log_buffer == nullptr || log_buffer_size == 0) { - *out_log_size = 0; - return; - } - - /* Get and clear our logs. */ - #if defined(AMS_SDMMC_USE_LOGGER) - { - if (this->error_logger.HasLog()) { - this->PushErrorTimeStamp(); - - *out_log_size = this->error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size); - } else { - *out_log_size = 0; - } - } - #else - { - *out_log_size = 0; - } - #endif - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp deleted file mode 100644 index 8cb0490..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp +++ /dev/null @@ -1,512 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" -#include "sdmmc_i_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - #if defined(AMS_SDMMC_USE_LOGGER) - class Logger { - private: - static constexpr size_t LogLengthMax = 0x20; - static constexpr size_t LogCountMax = 0x10; - private: - char logs[LogCountMax][LogLengthMax]; - int log_index; - private: - void Clear() { - for (size_t i = 0; i < LogCountMax; ++i) { - this->logs[i][0] = '\0'; - } - this->log_index = 0; - } - - size_t Pop(char *dst, size_t dst_size) { - /* Decrease log index. */ - if ((--this->log_index) < 0) { - this->log_index = LogCountMax - 1; - } - - /* Check if we have a log. */ - if (this->logs[this->log_index][0] == '\0') { - return 0; - } - - /* Copy log to output. */ - const int len = ::ams::util::Strlcpy(dst, this->logs[this->log_index], dst_size); - - /* Clear the log we copied. */ - this->logs[this->log_index][0] = '\0'; - - return static_cast(len); - } - - public: - Logger() { - this->Clear(); - } - - void Push(const char *fmt, std::va_list vl) { - /* Format the log into the current buffer. */ - ::ams::util::TVSNPrintf(this->logs[this->log_index], LogLengthMax, fmt, vl); - - /* Update our log index. */ - if ((++this->log_index) >= static_cast(LogCountMax)) { - this->log_index = 0; - } - } - - void Push(const char *fmt, ...) { - std::va_list vl; - va_start(vl, fmt); - this->Push(fmt, vl); - va_end(vl); - } - - bool HasLog() const { - const int index = this->log_index > 0 ? this->log_index - 1 : static_cast(LogCountMax - 1); - return this->logs[index][0] != '\0'; - } - - size_t GetAndClearLogs(char *dst, size_t dst_size) { - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size > 0); - - /* Pop logs until we run out of them. */ - size_t total_len = 0; - while (true) { - /* Pop the current log. */ - const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len); - if (cur_len == 0) { - break; - } - - /* Check if the log exceeded the buffer size. */ - if (total_len + cur_len + 1 >= dst_size) { - break; - } - - /* Advance the total length. */ - total_len += cur_len; - - /* Check if there's space for our separator. */ - if (total_len + 3 >= dst_size) { - break; - } - - dst[total_len + 0] = ','; - dst[total_len + 1] = ' '; - total_len += 2; - } - - /* Ensure that the complete log fits in the buffer. */ - if (total_len >= dst_size) { - total_len = dst_size - 1; - } - - /* Ensure null termination. */ - dst[total_len] = '\0'; - - /* Clear any remaining logs. */ - this->Clear(); - - /* Return the length of the logs, including null terminator. */ - return total_len + 1; - } - }; - #endif - - enum DeviceType { - DeviceType_Mmc = 0, - DeviceType_SdCard = 1, - DeviceType_GcAsic = 2, - }; - - enum DeviceState { - DeviceState_Idle = 0, - DeviceState_Ready = 1, - DeviceState_Ident = 2, - DeviceState_Stby = 3, - DeviceState_Tran = 4, - DeviceState_Data = 5, - DeviceState_Rcv = 6, - DeviceState_Prg = 7, - DeviceState_Dis = 8, - DeviceState_Rsvd0 = 9, - DeviceState_Rsvd1 = 10, - DeviceState_Rsvd2 = 11, - DeviceState_Rsvd3 = 12, - DeviceState_Rsvd4 = 13, - DeviceState_Rsvd5 = 14, - DeviceState_RsvdIoMode = 15, - DeviceState_Unknown = 16, - }; - - enum DeviceStatus : u32 { - DeviceStatus_AkeSeqError = (1u << 3), - DeviceStatus_AppCmd = (1u << 5), - DeviceStatus_SwitchError = (1u << 7), - DeviceStatus_EraseReset = (1u << 13), - DeviceStatus_WpEraseSkip = (1u << 15), - DeviceStatus_CidCsdOverwrite = (1u << 16), - DeviceStatus_Error = (1u << 19), - DeviceStatus_CcError = (1u << 20), - DeviceStatus_DeviceEccFailed = (1u << 21), - DeviceStatus_IllegalCommand = (1u << 22), - DeviceStatus_ComCrcError = (1u << 23), - DeviceStatus_LockUnlockFailed = (1u << 24), - DeviceStatus_WpViolation = (1u << 26), - DeviceStatus_EraseParam = (1u << 27), - DeviceStatus_EraseSeqError = (1u << 28), - DeviceStatus_BlockLenError = (1u << 29), - DeviceStatus_AddressMisaligned = (1u << 30), - DeviceStatus_AddressOutOfRange = (1u << 31), - - DeviceStatus_CurrentStateShift = 9, - DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift), - - DeviceStatus_ErrorMask = (DeviceStatus_SwitchError | - DeviceStatus_EraseReset | - DeviceStatus_WpEraseSkip | - DeviceStatus_CidCsdOverwrite | - DeviceStatus_Error | - DeviceStatus_CcError | - DeviceStatus_DeviceEccFailed | - DeviceStatus_IllegalCommand | - DeviceStatus_ComCrcError | - DeviceStatus_LockUnlockFailed | - DeviceStatus_WpViolation | - DeviceStatus_EraseParam | - DeviceStatus_EraseSeqError | - DeviceStatus_BlockLenError | - DeviceStatus_AddressMisaligned | - DeviceStatus_AddressOutOfRange), - }; - - class BaseDevice { - private: - u32 ocr; - u8 cid[DeviceCidSize]; - u16 csd[DeviceCsdSize / sizeof(u16)]; - u32 memory_capacity; - bool is_high_capacity; - bool is_valid_ocr; - bool is_valid_cid; - bool is_valid_csd; - bool is_valid_high_capacity; - bool is_valid_memory_capacity; - bool is_active; - bool is_awake; - public: - #if defined(AMS_SDMMC_THREAD_SAFE) - mutable os::Mutex device_mutex; - public: - BaseDevice() : device_mutex(true) - #else - BaseDevice() - #endif - { - this->is_awake = true; - this->ocr = 0; - this->memory_capacity = 0; - this->is_high_capacity = false; - this->OnDeactivate(); - } - - void OnDeactivate() { - this->is_active = false; - this->is_valid_ocr = false; - this->is_valid_cid = false; - this->is_valid_csd = false; - this->is_valid_high_capacity = false; - this->is_valid_memory_capacity = false; - } - - bool IsAwake() const { - return this->is_awake; - } - - void Awaken() { - this->is_awake = true; - } - - void PutToSleep() { - this->is_awake = false; - } - - bool IsActive() const { - return this->is_active; - } - - void SetActive() { - this->is_active = true; - } - - virtual void Deactivate() { - this->OnDeactivate(); - } - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual os::EventType *GetRemovedEvent() const = 0; - #endif - - virtual DeviceType GetDeviceType() const = 0; - virtual u16 GetRca() const = 0; - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - void InitializeRemovedEvent() { - if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { - os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear); - } - } - - void FinalizeRemovedEvent() { - if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { - os::FinalizeEvent(removed_event); - } - } - - void SignalRemovedEvent() { - if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { - os::SignalEvent(removed_event); - } - } - - void ClearRemovedEvent() { - if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { - os::ClearEvent(removed_event); - } - } - - bool IsRemoved() const { - if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) { - return os::TryWaitEvent(removed_event); - } - return false; - } - #endif - - Result CheckRemoved() const { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved()); - #endif - - return ResultSuccess(); - } - - Result CheckAccessible() const { - /* Check awake. */ - R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened()); - - /* Check active. */ - R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated()); - - /* Check removed. */ - R_TRY(this->CheckRemoved()); - - return ResultSuccess(); - } - - void SetHighCapacity(bool en) { - this->is_high_capacity = en; - this->is_valid_high_capacity = true; - } - - bool IsHighCapacity() const { - AMS_ABORT_UNLESS(this->is_valid_high_capacity); - return this->is_high_capacity; - } - - void SetOcr(u32 o) { - this->ocr = o; - this->is_valid_ocr = true; - } - - u32 GetOcr() const { - AMS_ABORT_UNLESS(this->is_valid_ocr); - return this->ocr; - } - - void SetCid(const void *src, size_t src_size) { - AMS_ABORT_UNLESS(src != nullptr); - AMS_ABORT_UNLESS(src_size >= DeviceCidSize); - std::memcpy(this->cid, src, DeviceCidSize); - this->is_valid_cid = true; - } - - void GetCid(void *dst, size_t dst_size) const { - AMS_ABORT_UNLESS(this->is_valid_cid); - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= DeviceCidSize); - std::memcpy(dst, this->cid, DeviceCidSize); - } - - void SetCsd(const void *src, size_t src_size) { - AMS_ABORT_UNLESS(src != nullptr); - AMS_ABORT_UNLESS(src_size >= DeviceCsdSize); - std::memcpy(this->csd, src, DeviceCsdSize); - this->is_valid_csd = true; - } - - void GetCsd(void *dst, size_t dst_size) const { - AMS_ABORT_UNLESS(this->is_valid_csd); - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize); - std::memcpy(dst, this->csd, DeviceCsdSize); - } - - void SetMemoryCapacity(u32 num_sectors) { - this->memory_capacity = num_sectors; - this->is_valid_memory_capacity = true; - } - - u32 GetMemoryCapacity() const { - AMS_ABORT_UNLESS(this->is_valid_memory_capacity); - return this->memory_capacity; - } - - void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const; - Result SetLegacyMemoryCapacity(); - - Result CheckDeviceStatus(u32 r1_resp) const; - DeviceState GetDeviceState(u32 r1_resp) const; - }; - - class BaseDeviceAccessor : public IDeviceAccessor { - private: - IHostController *host_controller; - BaseDevice *base_device; - u32 num_activation_failures; - u32 num_activation_error_corrections; - u32 num_read_write_failures; - u32 num_read_write_error_corrections; - #if defined(AMS_SDMMC_USE_LOGGER) - Logger error_logger; - #endif - private: - void ClearErrorInfo() { - this->num_activation_failures = 0; - this->num_activation_error_corrections = 0; - this->num_read_write_failures = 0; - this->num_read_write_error_corrections = 0; - } - protected: - explicit BaseDeviceAccessor(IHostController *hc) : host_controller(hc), base_device(nullptr) { - this->ClearErrorInfo(); - } - - IHostController *GetHostController() const { - return this->host_controller; - } - - void SetDevice(BaseDevice *bd) { - this->base_device = bd; - } - - Result CheckRemoved() const { - return this->base_device->CheckRemoved(); - } - - Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const; - - Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const { - u32 dummy; - return this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask); - } - - Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const { - return this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0); - } - - Result IssueCommandGoIdleState() const; - Result IssueCommandAllSendCid(void *dst, size_t dst_size) const; - Result IssueCommandSelectCard() const; - Result IssueCommandSendCsd(void *dst, size_t dst_size) const; - Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const; - - Result IssueCommandSendStatus(u32 status_ignore_mask) const { - u32 dummy; - return this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask); - } - - Result IssueCommandSendStatus() const { - return this->IssueCommandSendStatus(0); - } - - Result IssueCommandSetBlockLenToSectorSize() const; - Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const; - Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const; - Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read); - - void IncrementNumActivationErrorCorrections() { - ++this->num_activation_error_corrections; - } - - void PushErrorTimeStamp() { - #if defined(AMS_SDMMC_USE_LOGGER) - { - this->error_logger.Push("%u", static_cast(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds())); - } - #endif - } - - void PushErrorLog(bool with_timestamp, const char *fmt, ...) { - #if defined(AMS_SDMMC_USE_LOGGER) - { - std::va_list vl; - va_start(vl, fmt); - this->error_logger.Push(fmt, vl); - va_end(vl); - - if (with_timestamp) { - this->PushErrorTimeStamp(); - } - } - #else - { - AMS_UNUSED(with_timestamp, fmt); - } - #endif - } - - virtual Result OnActivate() = 0; - virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) = 0; - virtual Result ReStartup() = 0; - public: - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; - virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; - #endif - - virtual Result Activate() override; - virtual void Deactivate() override; - - virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) override; - virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) override; - - virtual Result GetMemoryCapacity(u32 *out_sectors) const override; - virtual Result GetDeviceStatus(u32 *out) const override; - virtual Result GetOcr(u32 *out) const override; - virtual Result GetRca(u16 *out) const override; - virtual Result GetCid(void *out, size_t size) const override; - virtual Result GetCsd(void *out, size_t size) const override; - - virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override; - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp deleted file mode 100644 index 1790380..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_clock_reset_controller.hpp" -#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp" -#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp" - -namespace ams::sdmmc::impl::ClockResetController { - - namespace { - - constinit bool g_is_module_initialized[Module_Count] = {}; - - #if defined(AMS_SDMMC_THREAD_SAFE) - - constinit os::SdkMutex g_module_mutex; - - #define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex) - - #else - - #define AMS_SDMMC_LOCK_MODULE_MUTEX() - - #endif - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - constinit bool g_is_pcv_control = false; - - #define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control) - - #else - - #define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false) - - #endif - - } - - void Initialize(Module module) { - /* Acquire exclusive access to module state. */ - AMS_SDMMC_LOCK_MODULE_MUTEX(); - - /* Mark the module as initialized. */ - g_is_module_initialized[module] = true; - - /* Initialize the module. */ - AMS_SDMMC_IF_IS_PCV_CONTROL() { - ClockResetController::pcv::Initialize(module); - } else { - ClockResetController::reg::Initialize(module); - } - } - - void Finalize(Module module) { - /* Acquire exclusive access to module state. */ - AMS_SDMMC_LOCK_MODULE_MUTEX(); - - /* Finalize the module. */ - AMS_SDMMC_IF_IS_PCV_CONTROL() { - ClockResetController::pcv::Finalize(module); - } else { - ClockResetController::reg::Finalize(module); - } - - /* Mark the module as finalized. */ - g_is_module_initialized[module] = false; - } - - bool IsAvailable(Module module) { - AMS_SDMMC_IF_IS_PCV_CONTROL() { - return ClockResetController::pcv::IsAvailable(module); - } else { - return ClockResetController::reg::IsAvailable(module); - } - } - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - void SwitchToPcvControl() { - /* Acquire exclusive access to module state. */ - AMS_SDMMC_LOCK_MODULE_MUTEX(); - - /* If we're already using pcv control, we don't need to do anything. */ - AMS_SDMMC_IF_IS_PCV_CONTROL() { - return; - } - - /* Finalize all modules. */ - for (int i = 0; i < Module_Count; ++i) { - if (g_is_module_initialized[i]) { - ClockResetController::reg::Finalize(static_cast(i)); - } - } - - /* Mark that we've switched to pcv control. */ - - /* Initialize modules using pcv control. */ - for (int i = 0; i < Module_Count; ++i) { - if (g_is_module_initialized[i]) { - ClockResetController::pcv::Initialize(static_cast(i)); - } - } - } - #endif - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { - AMS_SDMMC_IF_IS_PCV_CONTROL() { - return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency); - } else { - return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency); - } - } - - void AssertReset(Module module) { - AMS_SDMMC_IF_IS_PCV_CONTROL() { - return ClockResetController::pcv::AssertReset(module); - } else { - return ClockResetController::reg::AssertReset(module); - } - } - - void ReleaseReset(Module module, u32 target_frequency_khz) { - AMS_SDMMC_IF_IS_PCV_CONTROL() { - return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz); - } else { - return ClockResetController::reg::ReleaseReset(module, target_frequency_khz); - } - } - - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp deleted file mode 100644 index 29ac276..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::sdmmc::impl::ClockResetController { - - enum Module { - #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - Module_Sdmmc1 = 0, - Module_Sdmmc2 = 1, - Module_Sdmmc3 = 2, - Module_Sdmmc4 = 3, - #endif - - Module_Count, - }; - - void Initialize(Module module); - void Finalize(Module module); - bool IsAvailable(Module module); - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - void SwitchToPcvControl(); - #endif - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); - void AssertReset(Module module); - void ReleaseReset(Module module, u32 target_frequency_khz); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp deleted file mode 100644 index 8d8c39d..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp" - -namespace ams::sdmmc::impl::ClockResetController::pcv { - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - - void Initialize(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not implemented"); - } - - void Finalize(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not implemented"); - } - - bool IsAvailable(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not implemented"); - } - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { - AMS_UNUSED(out_actual_frequency, module, target_frequency); - AMS_ABORT("PCV Control not implemented"); - } - - void AssertReset(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not implemented"); - } - - void ReleaseReset(Module module, u32 target_frequency_khz) { - AMS_UNUSED(module, target_frequency_khz); - AMS_ABORT("PCV Control not implemented"); - } - - #else - - void Initialize(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not supported"); - } - - void Finalize(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not supported"); - } - - bool IsAvailable(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not supported"); - } - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) { - AMS_UNUSED(out_actual_frequency, module, target_frequency); - AMS_ABORT("PCV Control not supported"); - } - - void AssertReset(Module module) { - AMS_UNUSED(module); - AMS_ABORT("PCV Control not supported"); - } - - void ReleaseReset(Module module, u32 target_frequency_khz) { - AMS_UNUSED(module, target_frequency_khz); - AMS_ABORT("PCV Control not supported"); - } - - #endif - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp deleted file mode 100644 index 85144bf..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_clock_reset_controller.hpp" - -namespace ams::sdmmc::impl::ClockResetController::pcv { - - void Initialize(Module module); - void Finalize(Module module); - bool IsAvailable(Module module); - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); - void AssertReset(Module module); - void ReleaseReset(Module module, u32 target_frequency_khz); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp deleted file mode 100644 index 3a49428..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp" -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl::ClockResetController::reg { - - namespace { - - #if defined(AMS_SDMMC_THREAD_SAFE) - - constinit os::SdkMutex g_init_mutex; - - #define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex) - - #else - - #define AMS_SDMMC_LOCK_INIT_MUTEX() - - #endif - - constinit bool g_is_initialized = false; - - struct ModuleInfo { - u32 target_frequency_khz; - u32 actual_frequency_khz; - }; - - constinit ModuleInfo g_module_infos[Module_Count] = {}; - - constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000); - constexpr inline size_t ClockResetControllerRegistersSize = 4_KB; - - constinit uintptr_t g_clkrst_registers_address = 0; - - [[maybe_unused]] void InitializePllc4() { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */ - constexpr u32 ExpectedPllc4Base = 0x58006804; - if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) { - return; - } - - /* Disable PLLC4_ENABLE, if it's currently set. */ - if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) { - ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE)); - } - - /* Operate on the register with PLLC4_ENABLE cleared. */ - { - /* Clear IDDQ, read to be sure it takes, wait 5 us. */ - ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF)); - ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE); - WaitMicroSeconds(5); - - /* Write the expected value sans IDDQ/PLLC4_ENABLE. */ - constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE), - CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ)); - ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask); - } - - /* Write PLLC4_ENABLE, and read to be sure our configuration takes. */ - ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE)); - ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE); - - /* Wait up to 1s for changes to take. */ - { - ManualTimer timer(1000); - while (true) { - /* Check if we're done. */ - if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) { - break; - } - - /* Check that we haven't timed out. */ - AMS_ABORT_UNLESS(timer.Update()); - } - } - } - - void InitializeLegacyTmClk() { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Configure the legacy tm clock as 12MHz. */ - ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0), - CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66)); - - /* Enable clock to the legacy tm. */ - ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE)); - } - - bool IsResetReleased(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Get the reset bit from RST_DEVICES_* */ - switch (module) { - case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE)); - case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE)); - case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE)); - case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void SetReset(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Set reset in RST_DEV_*_SET */ - switch (module) { - case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE)); - case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE)); - case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE)); - case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void ClearReset(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Set reset in RST_DEV_*_CLR */ - switch (module) { - case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE)); - case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE)); - case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE)); - case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - bool IsClockEnabled(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Get the enable bit from CLK_OUT_ENB_* */ - switch (module) { - case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE)); - case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE)); - case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE)); - case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void SetClockEnable(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Set clock enable bit in CLK_ENB_*_SET */ - switch (module) { - case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE)); - case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE)); - case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE)); - case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void ClearClockEnable(Module module) { - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Set clock enable bit in CLK_ENB_*_CLR */ - switch (module) { - case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE)); - case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE)); - case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE)); - case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE)); - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) { - /* Check that we can write to output. */ - AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr); - - /* Determine frequency/divisor. */ - u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0)); - u8 n; - switch (target_frequency_khz) { - case 25'000: - *out_actual_frequency_khz = 24'728; - n = 31; - break; - case 26'000: - *out_actual_frequency_khz = 25'500; - n = 30; - break; - case 40'800: - *out_actual_frequency_khz = 40'800; - n = 18; - break; - case 50'000: - *out_actual_frequency_khz = 48'000; - n = 15; - break; - case 52'000: - *out_actual_frequency_khz = 51'000; - n = 14; - break; - case 100'000: - #if defined(AMS_SDMMC_SET_PLLC4_BASE) - *out_actual_frequency_khz = 99'840; - n = 2; - clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2)); - #else - *out_actual_frequency_khz = 90'667; - n = 7; - #endif - break; - case 200'000: - #if defined(AMS_SDMMC_SET_PLLC4_BASE) - *out_actual_frequency_khz = 199'680; - n = 0; - if (module == Module_Sdmmc2 || module == Module_Sdmmc4) { - clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ)); - } else { - clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2)); - } - #else - *out_actual_frequency_khz = 163'200; - n = 3; - #endif - break; - case 208'000: - *out_actual_frequency_khz = 204'000; - n = 2; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Set frequencies in module info. */ - g_module_infos[module].target_frequency_khz = target_frequency_khz; - g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz; - - /* Check that we have registers we can write to. */ - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Update the clock source. */ - switch (module) { - case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast(n)); break; - case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast(n)); break; - case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast(n)); break; - case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast(n)); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void EnsureControl(Module module) { - /* Read from RST_DEVICES_* to be sure previous configuration takes. */ - switch (module) { - case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; - case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; - case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break; - case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - } - - void Initialize(Module module) { - /* Initialization isn't module specific. */ - AMS_UNUSED(module); - - /* Acquire exclusive access to the initialization lock. */ - AMS_SDMMC_LOCK_INIT_MUTEX(); - - /* If we've already initialized, we don't need to do anything. */ - if (g_is_initialized) { - return; - } - - /* Clear module infos. */ - std::memset(g_module_infos, 0, sizeof(g_module_infos)); - - /* Get the registers address. */ - g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize); - AMS_ABORT_UNLESS(g_clkrst_registers_address != 0); - - /* Perform register initialization. */ - #if defined(AMS_SDMMC_SET_PLLC4_BASE) - InitializePllc4(); - #endif - InitializeLegacyTmClk(); - - /* Mark that we've initialized. */ - g_is_initialized = true; - } - - void Finalize(Module module) { - /* Nothing is needed for finalization. */ - AMS_UNUSED(module); - } - - bool IsAvailable(Module module) { - return IsResetReleased(module) && IsClockEnabled(module); - } - - void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) { - /* If we're not changing the clock frequency, we don't need to do anything. */ - if (target_frequency_khz == g_module_infos[module].target_frequency_khz) { - *out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz; - return; - } - - /* Temporarily disable clock. */ - const bool clock_enabled = IsClockEnabled(module); - if (clock_enabled) { - ClearClockEnable(module); - } - - /* Set the clock source. */ - SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz); - - /* Re-enable clock, if we should. */ - if (clock_enabled) { - SetClockEnable(module); - } - - /* Ensure that our configuration takes. */ - EnsureControl(module); - } - - void AssertReset(Module module) { - /* Set reset and disable clock. */ - SetReset(module); - ClearClockEnable(module); - - /* Ensure that our configuration takes. */ - EnsureControl(module); - } - - void ReleaseReset(Module module, u32 target_frequency_khz) { - /* Disable clock if it's enabled. */ - if (IsClockEnabled(module)) { - ClearClockEnable(module); - } - - /* Set reset. */ - SetReset(module); - - /* Set the clock source. */ - u32 actual_source_frequency_khz; - SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz); - - /* Enable clock. */ - SetClockEnable(module); - - /* Ensure that our configuration takes. */ - EnsureControl(module); - - /* Wait 100 clocks. */ - WaitClocks(100, actual_source_frequency_khz); - - /* Clear reset. */ - ClearReset(module); - - /* Ensure that our configuration takes. */ - EnsureControl(module); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp deleted file mode 100644 index 4043037..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_clock_reset_controller.hpp" - -namespace ams::sdmmc::impl::ClockResetController::reg { - - void Initialize(Module module); - void Finalize(Module module); - bool IsAvailable(Module module); - - void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency); - void AssertReset(Module module); - void ReleaseReset(Module module, u32 target_frequency_khz); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp deleted file mode 100644 index 3529b85..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) -#include "sdmmc_device_detector.hpp" - -namespace ams::sdmmc::impl { - - bool DeviceDetector::IsCurrentInserted() { - return gpio::GetValue(std::addressof(this->gpio_pad_session)) == this->inserted_gpio_value; - } - - void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) { - if (!prev_inserted && !cur_inserted) { - /* Not inserted -> Not inserted, nothing to do. */ - } else if (!prev_inserted && cur_inserted) { - /* Card was inserted. */ - if (this->callback_info.inserted_callback != nullptr) { - this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg); - } - } else if (prev_inserted && !cur_inserted) { - /* Card was removed. */ - if (this->callback_info.removed_callback != nullptr) { - this->callback_info.removed_callback(this->callback_info.removed_callback_arg); - } - } else /* if (prev_inserted && cur_inserted) */ { - /* Card was removed, and then inserted. */ - if (this->callback_info.removed_callback != nullptr) { - this->callback_info.removed_callback(this->callback_info.removed_callback_arg); - } - - if (this->callback_info.inserted_callback != nullptr) { - this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg); - } - } - } - - void DeviceDetector::DetectorThread() { - /* Initialize the gpio session. */ - sm::DoWithSession([] { gpio::Initialize(); }); - gpio::OpenSession(std::addressof(this->gpio_pad_session), this->gpio_device_code); - gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Input); - gpio::SetDebounceTime(std::addressof(this->gpio_pad_session), this->gpio_debounce_ms); - gpio::SetDebounceEnabled(std::addressof(this->gpio_pad_session), true); - gpio::SetInterruptMode(std::addressof(this->gpio_pad_session), gpio::InterruptMode_AnyEdge); - - /* Get the gpio session's interrupt event. */ - os::SystemEventType gpio_event; - R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(this->gpio_pad_session))); - - /* Initialize and link waitable holders. */ - os::WaitableManagerType wait_manager; - os::WaitableHolderType detector_thread_end_holder; - os::WaitableHolderType request_sleep_wake_event_holder; - os::WaitableHolderType gpio_event_holder; - os::InitializeWaitableManager(std::addressof(wait_manager)); - os::InitializeWaitableHolder(std::addressof(detector_thread_end_holder), std::addressof(this->detector_thread_end_event)); - os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(detector_thread_end_holder)); - os::InitializeWaitableHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(this->request_sleep_wake_event)); - os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(request_sleep_wake_event_holder)); - os::InitializeWaitableHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event)); - os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder)); - - /* Wait before detecting the initial state of the card. */ - os::SleepThread(TimeSpan::FromMilliSeconds(this->gpio_debounce_ms)); - bool cur_inserted = this->IsCurrentInserted(); - this->is_prev_inserted = cur_inserted; - - /* Set state as awake. */ - this->state = State_Awake; - os::SignalEvent(std::addressof(this->ready_device_status_event)); - - /* Enable interrupts to be informed of device status. */ - gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true); - - /* Wait, servicing our events. */ - while (true) { - /* Get the signaled holder. */ - os::WaitableHolderType *signaled_holder = os::WaitAny(std::addressof(wait_manager)); - - /* Process the holder. */ - bool insert_change = false; - if (signaled_holder == std::addressof(detector_thread_end_holder)) { - /* We should kill ourselves. */ - os::ClearEvent(std::addressof(this->detector_thread_end_event)); - this->state = State_Finalized; - break; - } else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) { - /* A request for us to sleep/wake has come in, so we'll acknowledge it. */ - os::ClearEvent(std::addressof(this->request_sleep_wake_event)); - this->state = State_Sleep; - os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event)); - - /* Temporarily unlink our interrupt event. */ - os::UnlinkWaitableHolder(std::addressof(gpio_event_holder)); - - /* Wait to be signaled. */ - signaled_holder = os::WaitAny(std::addressof(wait_manager)); - - /* Link our interrupt event back in. */ - os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder)); - - /* We're awake again. Either because we should exit, or because we were asked to wake up. */ - os::ClearEvent(std::addressof(this->request_sleep_wake_event)); - this->state = State_Awake; - os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event)); - - /* If we were asked to exit, do so. */ - if (signaled_holder == std::addressof(detector_thread_end_holder)) { - /* We should kill ourselves. */ - os::ClearEvent(std::addressof(this->detector_thread_end_event)); - this->state = State_Finalized; - break; - } else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ { - if ((this->force_detection) || - (({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), this->gpio_device_code)) && active; })) || - (os::TryWaitSystemEvent(std::addressof(gpio_event))) || - (this->is_prev_inserted != this->IsCurrentInserted())) - { - insert_change = true; - } - } - } else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ { - /* An event was detected. */ - insert_change = true; - } - - /* Handle an insert change, if one occurred. */ - if (insert_change) { - /* Call the relevant callback, if we have one. */ - if (this->device_detection_event_callback != nullptr) { - this->device_detection_event_callback(this->device_detection_event_callback_arg); - } - - /* Clear the interrupt event. */ - os::ClearSystemEvent(std::addressof(gpio_event)); - gpio::ClearInterruptStatus(std::addressof(this->gpio_pad_session)); - gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true); - - /* Update insertion status. */ - cur_inserted = this->IsCurrentInserted(); - this->HandleDeviceStatus(this->is_prev_inserted, cur_inserted); - this->is_prev_inserted = cur_inserted; - } - } - - /* Disable interrupts to our gpio event. */ - gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), false); - - /* Finalize and unlink waitable holders. */ - os::UnlinkWaitableHolder(std::addressof(gpio_event_holder)); - os::FinalizeWaitableHolder(std::addressof(gpio_event_holder)); - os::UnlinkWaitableHolder(std::addressof(request_sleep_wake_event_holder)); - os::FinalizeWaitableHolder(std::addressof(request_sleep_wake_event_holder)); - os::UnlinkWaitableHolder(std::addressof(detector_thread_end_holder)); - os::FinalizeWaitableHolder(std::addressof(detector_thread_end_holder)); - os::FinalizeWaitableManager(std::addressof(wait_manager)); - - /* Finalize the gpio session. */ - gpio::UnbindInterrupt(std::addressof(this->gpio_pad_session)); - gpio::CloseSession(std::addressof(this->gpio_pad_session)); - gpio::Finalize(); - } - - void DeviceDetector::Initialize(CallbackInfo *ci) { - /* Transition our state from finalized to initializing. */ - AMS_ABORT_UNLESS(this->state == State_Finalized); - this->state = State_Initializing; - - /* Set our callback infos. */ - this->callback_info = *ci; - - /* Initialize our events. */ - os::InitializeEvent(std::addressof(this->ready_device_status_event), false, os::EventClearMode_ManualClear); - os::InitializeEvent(std::addressof(this->request_sleep_wake_event), false, os::EventClearMode_ManualClear); - os::InitializeEvent(std::addressof(this->acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear); - os::InitializeEvent(std::addressof(this->detector_thread_end_event), false, os::EventClearMode_ManualClear); - - /* Create and start the detector thread. */ - os::CreateThread(std::addressof(this->detector_thread), DetectorThreadEntry, this, this->detector_thread_stack, sizeof(this->detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector)); - os::SetThreadNamePointer(std::addressof(this->detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector)); - os::StartThread(std::addressof(this->detector_thread)); - } - - void DeviceDetector::Finalize() { - /* Ensure we're not already finalized. */ - AMS_ABORT_UNLESS(this->state != State_Finalized); - - /* Signal event to end the detector thread. */ - os::SignalEvent(std::addressof(this->detector_thread_end_event)); - os::WaitThread(std::addressof(this->detector_thread)); - - /* Finalize thread and events. */ - os::DestroyThread(std::addressof(this->detector_thread)); - os::FinalizeEvent(std::addressof(this->ready_device_status_event)); - os::FinalizeEvent(std::addressof(this->request_sleep_wake_event)); - os::FinalizeEvent(std::addressof(this->acknowledge_sleep_awake_event)); - os::FinalizeEvent(std::addressof(this->detector_thread_end_event)); - } - - void DeviceDetector::PutToSleep() { - /* Signal request, wait for acknowledgement. */ - os::SignalEvent(std::addressof(this->request_sleep_wake_event)); - os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event)); - os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event)); - } - - void DeviceDetector::Awaken(bool force_det) { - /* Signal request, wait for acknowledgement. */ - this->force_detection = force_det; - os::SignalEvent(std::addressof(this->request_sleep_wake_event)); - os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event)); - os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event)); - } - - bool DeviceDetector::IsInserted() { - bool inserted = false; - - switch (this->state) { - case State_Initializing: - /* Wait for us to know whether the device is inserted. */ - os::WaitEvent(std::addressof(this->ready_device_status_event)); - [[fallthrough]]; - case State_Awake: - /* Get whether the device is currently inserted. */ - inserted = this->IsCurrentInserted(); - break; - case State_Sleep: - case State_Finalized: - /* Get whether the device was inserted when we last knew. */ - inserted = this->is_prev_inserted; - break; - } - - return inserted; - } - - void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) { - this->device_detection_event_callback_arg = arg; - this->device_detection_event_callback = cb; - } - - void DeviceDetector::UnregisterDetectionEventCallback() { - this->device_detection_event_callback = nullptr; - } - -} - -#endif diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp deleted file mode 100644 index a61f050..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) -#include - -namespace ams::sdmmc::impl { - - using InsertedCallback = void (*)(void *); - using RemovedCallback = void (*)(void *); - - struct CallbackInfo { - InsertedCallback inserted_callback; - void *inserted_callback_arg; - RemovedCallback removed_callback; - void *removed_callback_arg; - }; - - class DeviceDetector { - private: - enum State { - State_Initializing = 0, - State_Awake = 1, - State_Sleep = 2, - State_Finalized = 3, - }; - private: - alignas(os::ThreadStackAlignment) u8 detector_thread_stack[8_KB]; - State state; - bool is_prev_inserted; - bool force_detection; - os::ThreadType detector_thread; - os::EventType detector_thread_end_event; - os::EventType request_sleep_wake_event; - os::EventType acknowledge_sleep_awake_event; - os::EventType ready_device_status_event; - - DeviceCode gpio_device_code; - gpio::GpioValue inserted_gpio_value; - u32 gpio_debounce_ms; - gpio::GpioPadSession gpio_pad_session; - - CallbackInfo callback_info; - - DeviceDetectionEventCallback device_detection_event_callback; - void *device_detection_event_callback_arg; - private: - static void DetectorThreadEntry(void *arg) { - reinterpret_cast(arg)->DetectorThread(); - } - - void DetectorThread(); - bool IsCurrentInserted(); - void HandleDeviceStatus(bool prev_inserted, bool cur_inserted); - public: - explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd) - : gpio_device_code(dc), inserted_gpio_value(igv), gpio_debounce_ms(gd) - { - this->state = State_Finalized; - this->is_prev_inserted = false; - this->force_detection = false; - this->callback_info = {}; - this->device_detection_event_callback = nullptr; - this->device_detection_event_callback_arg = nullptr; - } - - void Initialize(CallbackInfo *ci); - void Finalize(); - - void PutToSleep(); - void Awaken(bool force_det); - - u32 GetDebounceMilliSeconds() const { - return this->gpio_debounce_ms; - } - - bool IsInserted(); - - void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg); - void UnregisterDetectionEventCallback(); - }; - -} - -#endif \ No newline at end of file diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp deleted file mode 100644 index a1ebe02..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_gc_asic_device_accessor.hpp" -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl { - - #if defined(AMS_SDMMC_THREAD_SAFE) - - #define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(this->gc_asic_device.device_mutex) - - #else - - #define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() - - #endif - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - - #define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!this->gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved()) - - #else - - #define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() - - #endif - - Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const { - /* Validate the operation buffer. */ - AMS_ABORT_UNLESS(op_buf != nullptr); - AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize); - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false); - TransferData xfer_data(const_cast(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data)); - if (R_FAILED(result)) { - /* We failed to write operation. Check if we were removed. */ - AMS_SDMMC_CHECK_GC_ASIC_REMOVED(); - - /* Determine what result we should return. */ - Result return_result = result; - { - /* Issue a stop transmission command. */ - u32 resp = 0; - result = hc->IssueStopTransmissionCommand(std::addressof(resp)); - if (R_SUCCEEDED(result)) { - /* If we successfully stopped transmission but have an error status, we prefer to return that. */ - result = this->gc_asic_device.CheckDeviceStatus(resp); - if (R_FAILED(result)) { - return_result = result; - } - } - - /* Check again if we were removed. */ - AMS_SDMMC_CHECK_GC_ASIC_REMOVED(); - - /* Request device status. */ - u32 device_status; - result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0); - - /* If we got a device status error here and we didn't previously, we prefer to return that. */ - if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) { - return_result = result; - } - } - return return_result; - } - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->gc_asic_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const { - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran)); - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::IssueCommandSleep() { - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran)); - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const { - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran)); - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::StartupGcAsicDevice() { - /* Start up the host controller. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false)); - - /* Wait 10 clocks for configuration to take. */ - WaitClocks(10, hc->GetDeviceClockFrequencyKHz()); - - /* Perform tuning with command index 21. */ - AMS_ABORT_UNLESS(hc->IsSupportedTuning()); - R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21)); - - /* Set the device as low capacity/no memory. */ - this->gc_asic_device.SetHighCapacity(false); - this->gc_asic_device.SetMemoryCapacity(0); - - /* Enable power saving. */ - hc->SetPowerSaving(true); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::OnActivate() { - /* If we fail to start up the device, ensure the host controller is shut down. */ - auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); }; - - /* Try to start up the device. */ - R_TRY(this->StartupGcAsicDevice()); - - /* We started up, so we don't need to power down. */ - power_guard.Cancel(); - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { - /* Check that we're not performing zero-byte rw. */ - AMS_ABORT_UNLESS(num_sectors > 0); - - /* Check that the buffer is big enough for the rw. */ - AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors); - - /* Perform the read/write. */ - u32 num_transferred_blocks; - R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read)); - - /* Require that we read/wrote as many sectors as we expected. */ - AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors); - - return ResultSuccess(); - } - - void GcAsicDeviceAccessor::Initialize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* If we've already initialized, we don't need to do anything. */ - if (this->is_initialized) { - return; - } - - /* Set the base device to our gc asic device. */ - BaseDeviceAccessor::SetDevice(std::addressof(this->gc_asic_device)); - - /* Initialize. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - this->gc_asic_device.InitializeRemovedEvent(); - hc->PreSetRemovedEvent(this->gc_asic_device.GetRemovedEvent()); - } - #endif - hc->Initialize(); - - /* Mark ourselves as initialized. */ - this->is_initialized = true; - } - - void GcAsicDeviceAccessor::Finalize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* If we've already finalized, we don't need to do anything. */ - if (!this->is_initialized) { - return; - } - this->is_initialized = false; - - /* Deactivate the device. */ - BaseDeviceAccessor::Deactivate(); - - /* Finalize the host controller. */ - BaseDeviceAccessor::GetHostController()->Finalize(); - - /* Finalize the removed event. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - this->gc_asic_device.FinalizeRemovedEvent(); - } - #endif - } - - Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { - /* Check that we can write to output. */ - AMS_ABORT_UNLESS(out_speed_mode != nullptr); - - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - *out_speed_mode = SpeedMode_GcAsicSpeed; - return ResultSuccess(); - } - - void GcAsicDeviceAccessor::PutGcAsicToSleep() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* If the device isn't awake, we don't need to do anything. */ - if (!this->gc_asic_device.IsAwake()) { - return; - } - - /* If necessary, put the host controller to sleep. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved()) - #else - if (this->gc_asic_device.IsActive()) - #endif - { - BaseDeviceAccessor::GetHostController()->PutToSleep(); - } - - /* Put the gc asic device to sleep. */ - this->gc_asic_device.PutToSleep(); - } - - Result GcAsicDeviceAccessor::AwakenGcAsic() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* If the device is awake, we don't need to do anything. */ - R_SUCCEED_IF(this->gc_asic_device.IsAwake()); - - /* Wake the device. */ - this->gc_asic_device.Awaken(); - - /* Wake the host controller, if we need to.*/ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved()) - #else - if (this->gc_asic_device.IsActive()) - #endif - { - R_TRY(BaseDeviceAccessor::GetHostController()->Awaken()); - } - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - /* Issue the command. */ - R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::FinishGcAsicOperation() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - /* Issue the command. */ - R_TRY(this->IssueCommandFinishOperation()); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::AbortGcAsicOperation() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - /* Issue stop transmission command. */ - u32 resp = 0; - R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp))); - R_TRY(this->gc_asic_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::SleepGcAsic() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - /* Issue the command. */ - R_TRY(this->IssueCommandSleep()); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result GcAsicDeviceAccessor::UpdateGcAsicKey() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->gc_asic_device.CheckAccessible()); - - /* Issue the command. */ - R_TRY(this->IssueCommandUpdateKey()); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp deleted file mode 100644 index c6c24ec..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_base_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - class GcAsicDevice : public BaseDevice { - private: - static constexpr u16 Rca = 0; - private: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - mutable os::EventType removed_event; - #endif - public: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual os::EventType *GetRemovedEvent() const override { - return std::addressof(this->removed_event); - } - #endif - - virtual DeviceType GetDeviceType() const override { - return DeviceType_GcAsic; - } - - virtual u16 GetRca() const override { - return Rca; - } - }; - - class GcAsicDeviceAccessor : public BaseDeviceAccessor { - private: - GcAsicDevice gc_asic_device; - bool is_initialized; - private: - Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const; - Result IssueCommandFinishOperation() const; - Result IssueCommandSleep(); - Result IssueCommandUpdateKey() const; - Result StartupGcAsicDevice(); - protected: - virtual Result OnActivate() override; - virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; - - virtual Result ReStartup() override { - AMS_ABORT("Can't ReStartup GcAsic\n"); - } - public: - virtual void Initialize() override; - virtual void Finalize() override; - virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; - public: - explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), is_initialized(false) { - /* ... */ - } - - void PutGcAsicToSleep(); - Result AwakenGcAsic(); - Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size); - Result FinishGcAsicOperation(); - Result AbortGcAsicOperation(); - Result SleepGcAsic(); - Result UpdateGcAsicKey(); - - void SignalGcRemovedEvent() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - this->gc_asic_device.SignalRemovedEvent(); - #else - AMS_ABORT("SignalGcRemovedEvent called without event support\n"); - #endif - } - - void ClearGcRemovedEvent() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - this->gc_asic_device.ClearRemovedEvent(); - #else - AMS_ABORT("ClearGcRemovedEvent called without event support\n"); - #endif - } - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp deleted file mode 100644 index 94e85bd..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" - -namespace ams::sdmmc::impl { - - class IDeviceAccessor { - public: - virtual void Initialize() = 0; - virtual void Finalize() = 0; - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; - virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; - #endif - - virtual Result Activate() = 0; - virtual void Deactivate() = 0; - - virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0; - virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0; - - virtual Result GetSpeedMode(SpeedMode *out) const = 0; - virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0; - virtual Result GetDeviceStatus(u32 *out) const = 0; - virtual Result GetOcr(u32 *out) const = 0; - virtual Result GetRca(u16 *out) const = 0; - virtual Result GetCid(void *out, size_t size) const = 0; - virtual Result GetCsd(void *out, size_t size) const = 0; - - virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0; - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp deleted file mode 100644 index f4439bb..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#if defined(AMS_SDMMC_USE_OS_EVENTS) -#include -#endif - -namespace ams::sdmmc::impl { - - enum ResponseType { - ResponseType_R0 = 0, - ResponseType_R1 = 1, - ResponseType_R2 = 2, - ResponseType_R3 = 3, - ResponseType_R6 = 4, - ResponseType_R7 = 5, - }; - - enum TransferDirection { - TransferDirection_ReadFromDevice = 0, - TransferDirection_WriteToDevice = 1, - }; - - enum CommandIndex { - /* Generic commands. */ - CommandIndex_GoIdleState = 0, - CommandIndex_SendOpCond = 1, - CommandIndex_AllSendCid = 2, - CommandIndex_SendRelativeAddr = 3, - CommandIndex_SetRelativeAddr = 3, - CommandIndex_SetDsr = 4, - - CommandIndex_Switch = 6, - CommandIndex_SelectCard = 7, - CommandIndex_DeselectCard = 7, - CommandIndex_SendIfCond = 8, - CommandIndex_SendExtCsd = 8, - CommandIndex_SendCsd = 9, - CommandIndex_SendCid = 10, - CommandIndex_VoltageSwitch = 11, - CommandIndex_StopTransmission = 12, - CommandIndex_SendStatus = 13, - CommandIndex_SendTaskStatus = 13, - - CommandIndex_GoInactiveState = 15, - CommandIndex_SetBlockLen = 16, - CommandIndex_ReadSingleBlock = 17, - CommandIndex_ReadMultipleBlock = 18, - CommandIndex_SendTuningBlock = 19, - CommandIndex_SpeedClassControl = 20, - - CommandIndex_AddressExtension = 22, - CommandIndex_SetBlockCount = 23, - CommandIndex_WriteBlock = 24, - CommandIndex_WriteMultipleBlock = 25, - - CommandIndex_ProgramCsd = 27, - CommandIndex_SetWriteProt = 28, - CommandIndex_ClearWriteProt = 29, - CommandIndex_SendWriteProt = 30, - - CommandIndex_EraseWriteBlockStart = 32, - CommandIndex_EraseWriteBlockEnd = 33, - - CommandIndex_EraseGroupStart = 35, - CommandIndex_EraseGroupEnd = 36, - - CommandIndex_Erase = 38, - - CommandIndex_LockUnlock = 42, - - CommandIndex_AppCmd = 55, - CommandIndex_GenCmd = 56, - - /* Nintendo specific vendor commands for lotus3. */ - CommandIndex_GcAsicWriteOperation = 60, - CommandIndex_GcAsicFinishOperation = 61, - CommandIndex_GcAsicSleep = 62, - CommandIndex_GcAsicUpdateKey = 63, - }; - - struct Command { - u32 command_index; - u32 command_argument; - ResponseType response_type; - bool is_busy; - - constexpr Command(u32 ci, u32 ca, ResponseType r, bool b) : command_index(ci), command_argument(ca), response_type(r), is_busy(b) { /* ... */ } - }; - - struct TransferData { - void *buffer; - size_t block_size; - u32 num_blocks; - TransferDirection transfer_direction; - bool is_multi_block_transfer; - bool is_stop_transmission_command_enabled; - - constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd, bool mb, bool st) - : buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(mb), is_stop_transmission_command_enabled(st) - { - if (this->num_blocks > 1) { - AMS_ABORT_UNLESS(this->is_multi_block_transfer); - } - } - - constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd) - : buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(false), is_stop_transmission_command_enabled(false) - { - AMS_ABORT_UNLESS(this->num_blocks == 1); - } - }; - - class IHostController { - public: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual void PreSetRemovedEvent(ams::os::EventType *event) = 0; - #endif - - virtual void Initialize() = 0; - virtual void Finalize() = 0; - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; - virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0; - #endif - - virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0; - - virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0; - virtual void Shutdown() = 0; - virtual void PutToSleep() = 0; - virtual Result Awaken() = 0; - - virtual Result SwitchToSdr12(); - - virtual bool IsSupportedBusPower(BusPower bus_power) const = 0; - virtual BusPower GetBusPower() const = 0; - - virtual bool IsSupportedBusWidth(BusWidth bus_width) const = 0; - virtual void SetBusWidth(BusWidth bus_width) = 0; - virtual BusWidth GetBusWidth() const = 0; - - virtual Result SetSpeedMode(SpeedMode speed_mode) = 0; - virtual SpeedMode GetSpeedMode() const = 0; - - virtual u32 GetDeviceClockFrequencyKHz() const = 0; - - virtual void SetPowerSaving(bool en) = 0; - virtual bool IsPowerSavingEnable() const = 0; - - virtual void EnableDeviceClock() = 0; - virtual void DisableDeviceClock() = 0; - virtual bool IsDeviceClockEnable() const = 0; - - virtual u32 GetMaxTransferNumBlocks() const = 0; - - virtual void ChangeCheckTransferInterval(u32 ms) = 0; - virtual void SetDefaultCheckTransferInterval() = 0; - - virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) = 0; - virtual Result IssueStopTransmissionCommand(u32 *out_response) = 0; - - ALWAYS_INLINE Result IssueCommand(const Command *command, TransferData *xfer_data) { - return this->IssueCommand(command, xfer_data, nullptr); - } - - ALWAYS_INLINE Result IssueCommand(const Command *command) { - return this->IssueCommand(command, nullptr, nullptr); - } - - virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const = 0; - virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const = 0; - - virtual bool IsSupportedTuning() const = 0; - virtual Result Tuning(SpeedMode speed_mode, u32 command_index) = 0; - virtual void SaveTuningStatusForHs400() = 0; - virtual Result GetInternalStatus() const = 0; - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp deleted file mode 100644 index bf36180..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp +++ /dev/null @@ -1,998 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_timer.hpp" -#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" -#include "sdmmc_io_impl.board.nintendo_nx.hpp" - -namespace ams::sdmmc::impl { - - /* Lovingly taken from libexosphere. */ - namespace i2c_impl { - - enum Port { - Port_1 = 0, - /* TODO: Support other ports? */ - Port_5 = 4, - - Port_Count = 5, - }; - - constexpr inline const dd::PhysicalAddress I2c5RegistersAddress = UINT64_C(0x7000D000); - constexpr inline const size_t I2c5RegistersSize = 4_KB; - - namespace { - - constexpr inline size_t MaxTransferSize = sizeof(u32); - - constinit std::array g_register_addresses = [] { - std::array arr = {}; - return arr; - }(); - - void LoadConfig(uintptr_t address) { - /* Configure for TIMEOUT and MSTR config load. */ - /* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */ - /* We will reproduce the write just in case it is undocumented. */ - reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1), - I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE), - I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE), - I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); - - /* Wait up to 20 microseconds for the master config to be loaded. */ - for (int i = 0; i < 20; ++i) { - if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) { - return; - } - util::WaitMicroSeconds(1); - } - } - - void ClearBus(uintptr_t address) { - /* Configure the bus clear register. */ - reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9), - I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP), - I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE), - I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); - - /* Load the config. */ - LoadConfig(address); - - /* Wait up to 250us (in 25 us increments) until the bus clear is done. */ - for (int i = 0; i < 10; ++i) { - if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) { - break; - } - - util::WaitMicroSeconds(25); - } - - /* Read the bus clear status. */ - reg::Read(address + I2C_BUS_CLEAR_STATUS); - } - - void InitializePort(uintptr_t address) { - /* Calculate the divisor. */ - constexpr int Divisor = util::DivideUp(19200, 8 * 400); - - /* Set the divisor. */ - reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1), - I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1)); - - /* Clear the bus. */ - ClearBus(address); - - /* Clear the status. */ - reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER)); - } - - bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { - AMS_UNUSED(port, unused); - - /* Ensure we don't write too much. */ - u32 data = 0; - if (src_size > MaxTransferSize) { - return false; - } - - /* Copy the data to a transfer word. */ - std::memcpy(std::addressof(data), src, src_size); - - - /* Configure the to write the 7-bit address. */ - reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), - I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE)); - - /* Configure to write the data. */ - reg::Write(base_address + I2C_I2C_CMD_DATA1, data); - - /* Configure to write the correct amount of data. */ - reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), - I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), - I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE), - I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1)); - - /* Load the configuration. */ - LoadConfig(base_address); - - /* Start the command. */ - reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); - - /* Wait for the command to be done. */ - while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } - - /* Check if the transfer was successful. */ - return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL)); - } - - bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { - AMS_UNUSED(port, unused); - - /* Ensure we don't read too much. */ - if (dst_size > MaxTransferSize) { - return false; - } - - /* Configure the to read the 7-bit address. */ - reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), - I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ)); - - /* Configure to read the correct amount of data. */ - reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), - I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), - I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ), - I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1)); - - /* Load the configuration. */ - LoadConfig(base_address); - - /* Start the command. */ - reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); - - /* Wait for the command to be done. */ - while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } - - /* Check that the transfer was successful. */ - if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) { - return false; - } - - /* Read and copy out the data. */ - u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1); - std::memcpy(dst, std::addressof(data), dst_size); - return true; - } - - } - - void SetRegisterAddress(Port port, uintptr_t address) { - g_register_addresses[port] = address; - } - - void Initialize(Port port) { - InitializePort(g_register_addresses[port]); - } - - bool Query(void *dst, size_t dst_size, Port port, int address, int r) { - const uintptr_t base_address = g_register_addresses[port]; - - /* Select the register we want to read. */ - bool success = Write(base_address, port, address, std::addressof(r), 1, false); - if (success) { - /* If we successfully selected, read data from the register. */ - success = Read(base_address, port, dst, dst_size, address, true); - } - - return success; - } - - bool Send(Port port, int address, int r, const void *src, size_t src_size) { - const uintptr_t base_address = g_register_addresses[port]; - - /* Create a transfer buffer, make sure we can use it. */ - u8 buffer[MaxTransferSize]; - if (src_size > sizeof(buffer) - 1) { - return false; - } - - /* Copy data into the buffer. */ - buffer[0] = static_cast(r); - std::memcpy(buffer + 1, src, src_size); - - return Write(base_address, port, address, buffer, src_size + 1, false); - } - - } - - namespace max7762x { - - /* The only regulator we care about for SD card power is ldo2. */ - constexpr inline const u8 Max77620PwrI2cAddr = 0x3C; - - constexpr inline const u32 Max77620Ldo2MvStep = 50'000; /* 50 mV (50K uV) steps. */ - constexpr inline const u32 Max77620Ldo2MvMin = 800'000; /* 0.8V min voltage. */ - constexpr inline const u32 Max77620Ldo2MvDefault = 1'800'000; /* 1.8V default voltage. */ - constexpr inline const u32 Max77620Ldo2MvMax = 3'300'000; /* 3.3V max voltage. */ - constexpr inline const u8 Max77620Ldo2VoltAddr = 0x27; - constexpr inline const u8 Max77620Ldo2CfgAddr = 0x28; - constexpr inline const u8 Max77620Ldo2VoltMask = 0x3F; - constexpr inline const u8 Max77620Ldo2EnableMask = 0xC0; - constexpr inline const u8 Max77620Ldo2EnableShift = 0x6; - constexpr inline const u8 Max77620Ldo2StatusMask = 0x00; - - namespace { - - #if defined(AMS_SDMMC_THREAD_SAFE) - constinit os::SdkMutex g_i2c_init_mutex; - - #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex) - - #else - - #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() - - #endif - - constinit bool g_initialized_i2c = false; - - void EnsureI2cInitialized() { - if (AMS_UNLIKELY(!g_initialized_i2c)) { - /* Ensure we have exclusive access to the i2c init status. */ - AMS_SDMMC_LOCK_I2C_INIT_MUTEX(); - - if (AMS_LIKELY(!g_initialized_i2c)) { - i2c_impl::SetRegisterAddress(i2c_impl::Port_5, dd::QueryIoMapping(i2c_impl::I2c5RegistersAddress, i2c_impl::I2c5RegistersSize)); - i2c_impl::Initialize(i2c_impl::Port_5); - g_initialized_i2c = true; - } - } - } - - } - - bool SetVoltageEnabled(bool en) { - /* Ensure that we can use i2c to communicate with the max7762x regulator. */ - EnsureI2cInitialized(); - - /* Read the current value. */ - u8 val; - if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { - return false; - } - - /* Set or clear the enable mask. */ - val &= ~Max77620Ldo2EnableMask; - if (en) { - val |= ((3 << Max77620Ldo2EnableShift) & Max77620Ldo2EnableMask); - } - - /* Write the updated value. */ - if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { - return false; - } - - /* Wait 1ms for change to take. */ - WaitMicroSeconds(1); - - /* Voltage is now enabled/disabled. */ - return true; - } - - bool SetVoltageValue(u32 micro_volts) { - /* Ensure that we can use i2c to communicate with the max7762x regulator. */ - EnsureI2cInitialized(); - - /* Check that the value is within range. */ - if (micro_volts < Max77620Ldo2MvMin || Max77620Ldo2MvMax < micro_volts) { - return false; - } - - /* Determine the mult. */ - const u32 mult = util::DivideUp(micro_volts - Max77620Ldo2MvMin, Max77620Ldo2MvStep); - - /* Read the current value. */ - u8 val; - if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { - return false; - } - - /* Set the new voltage. */ - val &= ~Max77620Ldo2VoltMask; - val |= (mult & Max77620Ldo2VoltMask); - - /* Write the updated value. */ - if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { - return false; - } - - /* Wait 1ms for change to take. */ - WaitMicroSeconds(1); - - /* Voltage is now set. */ - return true; - } - - } - - Result SetSdCardVoltageEnabled(bool en) { - /* TODO: A way for this to be non-fatal? */ - AMS_ABORT_UNLESS(max7762x::SetVoltageEnabled(en)); - - return ResultSuccess(); - } - - Result SetSdCardVoltageValue(u32 micro_volts) { - /* TODO: A way for this to be non-fatal? */ - AMS_ABORT_UNLESS(max7762x::SetVoltageValue(micro_volts)); - - return ResultSuccess(); - } - - namespace gpio_impl { - - namespace { - - constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000d000; - constexpr inline size_t GpioRegistersSize = 4_KB; - - enum GpioPadPort { - GpioPadPort_A = 0, - GpioPadPort_B = 1, - GpioPadPort_C = 2, - GpioPadPort_D = 3, - GpioPadPort_E = 4, - GpioPadPort_F = 5, - GpioPadPort_G = 6, - GpioPadPort_H = 7, - GpioPadPort_I = 8, - GpioPadPort_J = 9, - GpioPadPort_K = 10, - GpioPadPort_L = 11, - GpioPadPort_M = 12, - GpioPadPort_N = 13, - GpioPadPort_O = 14, - GpioPadPort_P = 15, - GpioPadPort_Q = 16, - GpioPadPort_R = 17, - GpioPadPort_S = 18, - GpioPadPort_T = 19, - GpioPadPort_U = 20, - GpioPadPort_V = 21, - GpioPadPort_W = 22, - GpioPadPort_X = 23, - GpioPadPort_Y = 24, - GpioPadPort_Z = 25, - GpioPadPort_AA = 26, - GpioPadPort_BB = 27, - GpioPadPort_CC = 28, - GpioPadPort_DD = 29, - GpioPadPort_EE = 30, - GpioPadPort_FF = 31, - }; - - consteval unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { - AMS_ASSUME(which < 8); - - return (static_cast(port) * 8) + which; - } - - enum InternalGpioPadNumber { - InternalGpioPadNumber_E4 = GetInternalGpioPadNumber(GpioPadPort_E, 4), - InternalGpioPadNumber_M0 = GetInternalGpioPadNumber(GpioPadPort_M, 0), - }; - - constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { - return (number >> 5); - } - - constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { - return (number >> 3); - } - - constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { - return (number & 7); - } - - constexpr int ConvertPortNumberToOffset(int port_number) { - return (port_number & 3); - } - - struct PadNameToInternalPadNumberEntry { - GpioPadName pad_name; - InternalGpioPadNumber internal_number; - }; - - constexpr inline const PadNameToInternalPadNumberEntry PadNameToInternalPadNumberTable[] = { - { GpioPadName_PowSdEn, InternalGpioPadNumber_E4 }, - }; - - constexpr InternalGpioPadNumber ConvertPadNameToInternalPadNumber(GpioPadName pad) { - const PadNameToInternalPadNumberEntry *target = nullptr; - for (const auto &entry : PadNameToInternalPadNumberTable) { - if (entry.pad_name == pad) { - target = std::addressof(entry); - break; - } - } - - AMS_ABORT_UNLESS(target != nullptr); - return target->internal_number; - } - - enum GpioRegisterType { - GpioRegisterType_GPIO_CNF = 0, - GpioRegisterType_GPIO_OE = 1, - GpioRegisterType_GPIO_OUT = 2, - GpioRegisterType_GPIO_IN = 3, - GpioRegisterType_GPIO_INT_STA = 4, - GpioRegisterType_GPIO_INT_ENB = 5, - GpioRegisterType_GPIO_INT_LVL = 6, - GpioRegisterType_GPIO_INT_CLR = 7, - GpioRegisterType_GPIO_DB_CTRL = 8, - GpioRegisterType_GPIO_DB_CNT = 9, - }; - - constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; - constexpr inline int MaskedWriteBitOffset = 8; - - constexpr uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { - const auto controller = ConvertInternalGpioPadNumberToController(pad_number); - const auto port = ConvertInternalGpioPadNumberToPort(pad_number); - const auto offset = ConvertPortNumberToOffset(port); - - switch (reg_type) { - default: - return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); - case GpioRegisterType_GPIO_DB_CTRL: - return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); - case GpioRegisterType_GPIO_DB_CNT: - return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); - } - } - - void SetMaskedBit(uintptr_t pad_address, int index, int value) { - const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; - - reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); - } - - void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { - const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; - - reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); - } - - } - - void OpenSession(GpioPadName pad) { - /* Convert the pad to an internal number. */ - const auto pad_number = ConvertPadNameToInternalPadNumber(pad); - - /* Get the gpio registers address. */ - const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); - - /* Configure the pad as GPIO by setting the appropriate bit in CNF. */ - const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_CNF, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, 1); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - } - - void CloseSession(GpioPadName pad) { - /* Nothing needs to be done here, as the only thing official code does is unbind the interrupt event. */ - AMS_UNUSED(pad); - } - - void SetDirection(GpioPadName pad, Direction direction) { - /* Convert the pad to an internal number. */ - const auto pad_number = ConvertPadNameToInternalPadNumber(pad); - - /* Get the gpio registers address. */ - const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); - - /* Configure the pad direction modifying the appropriate bit in OE. */ - const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OE, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, direction); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - } - - void SetValue(GpioPadName pad, GpioValue value) { - /* Convert the pad to an internal number. */ - const auto pad_number = ConvertPadNameToInternalPadNumber(pad); - - /* Get the gpio registers address. */ - const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); - - /* Configure the pad value modifying the appropriate bit in OUT. */ - const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OUT, pad_number); - const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); - SetMaskedBit(pad_address, pad_index, value); - - /* Read the pad address to make sure our configuration takes. */ - reg::Read(pad_address); - } - - } - - namespace pinmux_impl { - - namespace { - - constexpr auto Sdmmc1ClkCmdDat03PadNumber = gpio_impl::InternalGpioPadNumber_M0; - - constexpr unsigned int Sdmmc1ClkCmdDat03PadMask = 0x3F; - - constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfGpio = 0x3F; - constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfSfio = 0x00; - - constexpr unsigned int Sdmmc1ClkCmdDat03PadOutHigh = 0x3F; - constexpr unsigned int Sdmmc1ClkCmdDat03PadOutLow = 0x00; - - constexpr unsigned int Sdmmc1ClkCmdDat03PadOeOutput = 0x3F; - constexpr unsigned int Sdmmc1ClkCmdDat03PadOeInput = 0x00; - - struct PinmuxDefinition { - u32 reg_offset; - u32 mask_val; - u32 pm_val; - }; - - /* NOTE: We only use the SDMMC1 pins, which are conveniently the first few... */ - constexpr const PinmuxDefinition PinmuxDefinitionMap[] = { - {0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */ - {0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */ - {0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */ - {0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */ - {0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */ - {0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */ - }; - - enum PinmuxPadIndex { - PinmuxPadIndex_Sdmmc1Clk = 0, - PinmuxPadIndex_Sdmmc1Cmd = 1, - PinmuxPadIndex_Sdmmc1Dat3 = 2, - PinmuxPadIndex_Sdmmc1Dat2 = 3, - PinmuxPadIndex_Sdmmc1Dat1 = 4, - PinmuxPadIndex_Sdmmc1Dat0 = 5, - - PinmuxPadIndex_Count, - }; - - static_assert(util::size(PinmuxDefinitionMap) == PinmuxPadIndex_Count); - - consteval const PinmuxDefinition GetDefinition(PinmuxPadIndex pad_index) { - AMS_ABORT_UNLESS(pad_index < PinmuxPadIndex_Count); - - return PinmuxDefinitionMap[pad_index]; - } - - template - ALWAYS_INLINE u32 UpdatePinmuxPad(uintptr_t pinmux_base_vaddr) { - constexpr const PinmuxDefinition Definition = GetDefinition(PadIndex); - - /* Fetch this PINMUX's register offset */ - constexpr u32 PinmuxRegOffset = Definition.reg_offset; - - /* Fetch this PINMUX's mask value */ - constexpr u32 PinmuxMaskVal = Definition.mask_val; - - /* Get current register ptr. */ - const uintptr_t pinmux_reg = pinmux_base_vaddr + PinmuxRegOffset; - - /* Read from the PINMUX register */ - u32 pinmux_val = reg::Read(pinmux_reg); - - /* This PINMUX register is locked */ - AMS_ABORT_UNLESS((pinmux_val & 0x80) == 0); - - constexpr u32 PmVal = (PinmuxConfigVal & 0x07); - - /* Adjust PM */ - if constexpr (PinmuxConfigMaskVal & 0x07) { - /* Apply additional changes first */ - if constexpr (PmVal == 0x05) { - /* This pin supports PUPD change */ - if constexpr (PinmuxMaskVal & 0x0C) { - /* Change PUPD */ - if ((pinmux_val & 0x0C) != 0x04) { - pinmux_val &= 0xFFFFFFF3; - pinmux_val |= 0x04; - } - } - - /* This pin supports Tristate change */ - if constexpr (PinmuxMaskVal & 0x10) { - /* Change Tristate */ - if (!(pinmux_val & 0x10)) { - pinmux_val |= 0x10; - } - } - - /* This pin supports EInput change */ - if constexpr (PinmuxMaskVal & 0x40) { - /* Change EInput */ - if (pinmux_val & 0x40) { - pinmux_val &= 0xFFFFFFBF; - } - } - } - - /* Translate PM value if necessary */ - constexpr u32 TranslatedPmVal = (PmVal == 0x04 || PmVal == 0x05 || PmVal >= 0x06) ? Definition.pm_val : PmVal; - - /* This pin supports PM change */ - if constexpr (PinmuxMaskVal & 0x03) { - /* Change PM */ - if ((pinmux_val & 0x03) != (TranslatedPmVal & 0x03)) { - pinmux_val &= 0xFFFFFFFC; - pinmux_val |= (TranslatedPmVal & 0x03); - } - } - } - - constexpr u32 PupdConfigVal = (PinmuxConfigVal & 0x18); - - /* Adjust PUPD */ - if constexpr (PinmuxConfigMaskVal & 0x18) { - if constexpr (PupdConfigVal < 0x11) { - /* This pin supports PUPD change */ - if constexpr (PinmuxMaskVal & 0x0C) { - /* Change PUPD */ - if (((pinmux_val >> 0x02) & 0x03) != (PupdConfigVal >> 0x03)) { - pinmux_val &= 0xFFFFFFF3; - pinmux_val |= (PupdConfigVal >> 0x01); - } - } - } - } - - constexpr u32 EodConfigVal = (PinmuxConfigVal & 0x60); - - /* Adjust EOd field */ - if constexpr (PinmuxConfigMaskVal & 0x60) { - if constexpr (EodConfigVal == 0x20) { - /* This pin supports Tristate change */ - if constexpr (PinmuxMaskVal & 0x10) { - /* Change Tristate */ - if (!(pinmux_val & 0x10)) { - pinmux_val |= 0x10; - } - } - - /* This pin supports EInput change */ - if constexpr (PinmuxMaskVal & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if constexpr (PinmuxMaskVal & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } else if constexpr (EodConfigVal == 0x40) { - /* This pin supports Tristate change */ - if constexpr (PinmuxMaskVal & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if constexpr (PinmuxMaskVal & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if constexpr (PinmuxMaskVal & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } else if constexpr (EodConfigVal == 0x60) { - /* This pin supports Tristate change */ - if constexpr (PinmuxMaskVal & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if constexpr (PinmuxMaskVal & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if constexpr (PinmuxMaskVal & 0x800) { - /* Change EOd */ - if (!(pinmux_val & 0x800)) { - pinmux_val |= 0x800; - } - } - } else { - /* This pin supports Tristate change */ - if constexpr (PinmuxMaskVal & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if constexpr (PinmuxMaskVal & 0x40) { - /* Change EInput */ - if (pinmux_val & 0x40) { - pinmux_val &= 0xFFFFFFBF; - } - } - - /* This pin supports EOd change */ - if constexpr (PinmuxMaskVal & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } - } - - constexpr u32 LockConfigVal = (PinmuxConfigVal & 0x80); - - /* Adjust Lock */ - if constexpr (PinmuxConfigMaskVal & 0x80) { - /* This pin supports Lock change */ - if constexpr (PinmuxMaskVal & 0x80) { - /* Change Lock */ - if ((pinmux_val ^ PinmuxConfigVal) & 0x80) { - pinmux_val &= 0xFFFFFF7F; - pinmux_val |= LockConfigVal; - } - } - } - - constexpr u32 IoResetConfigVal = (((PinmuxConfigVal >> 0x08) & 0x1) << 0x10); - - /* Adjust IoReset */ - if constexpr (PinmuxConfigMaskVal & 0x100) { - /* This pin supports IoReset change */ - if constexpr (PinmuxMaskVal & 0x10000) { - /* Change IoReset */ - if (((pinmux_val >> 0x10) ^ (PinmuxConfigVal >> 0x08)) & 0x01) { - pinmux_val &= 0xFFFEFFFF; - pinmux_val |= IoResetConfigVal; - } - } - } - - constexpr u32 ParkConfigVal = (((PinmuxConfigVal >> 0x0A) & 0x1) << 0x5); - - /* Adjust Park */ - if constexpr (PinmuxConfigMaskVal & 0x400) { - /* This pin supports Park change */ - if constexpr (PinmuxMaskVal & 0x20) { - /* Change Park */ - if (((pinmux_val >> 0x05) ^ (PinmuxConfigVal >> 0x0A)) & 0x01) { - pinmux_val &= 0xFFFFFFDF; - pinmux_val |= ParkConfigVal; - } - } - } - - constexpr u32 ElpdrConfigVal = (((PinmuxConfigVal >> 0x0B) & 0x1) << 0x08); - - /* Adjust ELpdr */ - if constexpr (PinmuxConfigMaskVal & 0x800) { - /* This pin supports ELpdr change */ - if constexpr (PinmuxMaskVal & 0x100) { - /* Change ELpdr */ - if (((pinmux_val >> 0x08) ^ (PinmuxConfigVal >> 0x0B)) & 0x01) { - pinmux_val &= 0xFFFFFEFF; - pinmux_val |= ElpdrConfigVal; - } - } - } - - constexpr u32 EhsmConfigVal = (((PinmuxConfigVal >> 0x0C) & 0x1) << 0x09); - - /* Adjust EHsm */ - if constexpr (PinmuxConfigMaskVal & 0x1000) { - /* This pin supports EHsm change */ - if constexpr (PinmuxMaskVal & 0x200) { - /* Change EHsm */ - if (((pinmux_val >> 0x09) ^ (PinmuxConfigVal >> 0x0C)) & 0x01) { - pinmux_val &= 0xFFFFFDFF; - pinmux_val |= EhsmConfigVal; - } - } - } - - constexpr u32 EIoHvConfigVal = (((PinmuxConfigVal >> 0x09) & 0x1) << 0x0A); - - /* Adjust EIoHv */ - if constexpr (PinmuxConfigMaskVal & 0x200) { - /* This pin supports EIoHv change */ - if constexpr (PinmuxMaskVal & 0x400) { - /* Change EIoHv */ - if (((pinmux_val >> 0x0A) ^ (PinmuxConfigVal >> 0x09)) & 0x01) { - pinmux_val &= 0xFFFFFBFF; - pinmux_val |= EIoHvConfigVal; - } - } - } - - constexpr u32 EschmtConfigVal = (((PinmuxConfigVal >> 0x0D) & 0x1) << 0x0C); - - /* Adjust ESchmt */ - if constexpr (PinmuxConfigMaskVal & 0x2000) { - /* This pin supports ESchmt change */ - if constexpr (PinmuxMaskVal & 0x1000) { - /* Change ESchmt */ - if (((pinmux_val >> 0x0C) ^ (PinmuxConfigVal >> 0x0D)) & 0x01) { - pinmux_val &= 0xFFFFEFFF; - pinmux_val |= EschmtConfigVal; - } - } - } - - constexpr u32 PreempConfigVal = (((PinmuxConfigVal >> 0x10) & 0x1) << 0xF); - - /* Adjust Preemp */ - if constexpr (PinmuxConfigMaskVal & 0x10000) { - /* This pin supports Preemp change */ - if constexpr (PinmuxMaskVal & 0x8000) { - /* Change Preemp */ - if (((pinmux_val >> 0x0F) ^ (PinmuxConfigVal >> 0x10)) & 0x01) { - pinmux_val &= 0xFFFF7FFF; - pinmux_val |= PreempConfigVal; - } - } - } - - constexpr u32 DrvTypeConfigVal = (((PinmuxConfigVal >> 0x0E) & 0x3) << 0xD); - - /* Adjust DrvType */ - if constexpr (PinmuxConfigMaskVal & 0xC000) { - /* This pin supports DrvType change */ - if constexpr (PinmuxMaskVal & 0x6000) { - /* Change DrvType */ - if (((pinmux_val >> 0x0D) ^ (PinmuxConfigVal >> 0x0E)) & 0x03) { - pinmux_val &= 0xFFFF9FFF; - pinmux_val |= DrvTypeConfigVal; - } - } - } - - /* Write to the appropriate PINMUX register */ - reg::Write(pinmux_reg, pinmux_val); - - /* Do a dummy read from the PINMUX register */ - pinmux_val = reg::Read(pinmux_reg); - - return pinmux_val; - } - - } - - void SetPinAssignment(PinAssignment assignment) { - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - AMS_UNUSED(apb_address); - - /* Set the pin assignment. */ - switch (assignment) { - case PinAssignment_Sdmmc1OutputHigh: - { - /* Clear Sdmmc1Clk pulldown. */ - UpdatePinmuxPad(apb_address); - - /* Get the gpio registers address. */ - const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as gpio. */ - const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfGpio); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as high. */ - const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutHigh); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as output. */ - const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeOutput); - - /* Read to be sure that our configuration takes. */ - reg::Read(oe_address); - } - break; - case PinAssignment_Sdmmc1ResetState: - { - /* Get the gpio registers address. */ - const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as sfio. */ - const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfSfio); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as low. */ - const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutLow); - - /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as input. */ - const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); - gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeInput); - - /* Read to be sure that our configuration takes. */ - reg::Read(oe_address); - - /* Set Sdmmc1Clk pulldown. */ - UpdatePinmuxPad(apb_address); - } - break; - case PinAssignment_Sdmmc1SchmtEnable: - { - /* Set Schmitt enable for all pins in the group. */ - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - } - break; - case PinAssignment_Sdmmc1SchmtDisable: - { - /* Set Schmitt disable for all pins in the group. */ - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - UpdatePinmuxPad(apb_address); - } - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp deleted file mode 100644 index 89b6230..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::sdmmc::impl { - - Result SetSdCardVoltageEnabled(bool en); - Result SetSdCardVoltageValue(u32 micro_volts); - - namespace pinmux_impl { - - enum PinAssignment { - PinAssignment_Sdmmc1OutputHigh = 2, - PinAssignment_Sdmmc1ResetState = 3, - PinAssignment_Sdmmc1SchmtEnable = 4, - PinAssignment_Sdmmc1SchmtDisable = 5, - }; - - void SetPinAssignment(PinAssignment assignment); - - } - - namespace gpio_impl { - - enum GpioValue { - GpioValue_Low = 0, - GpioValue_High = 1 - }; - - enum Direction { - Direction_Input = 0, - Direction_Output = 1, - }; - - enum GpioPadName { - GpioPadName_PowSdEn = 2, - }; - - void OpenSession(GpioPadName pad); - void CloseSession(GpioPadName pad); - - void SetDirection(GpioPadName pad, Direction direction); - void SetValue(GpioPadName pad, GpioValue value); - - - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp deleted file mode 100644 index 7468aab..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_mmc_device_accessor.hpp" -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl { - - #if defined(AMS_SDMMC_THREAD_SAFE) - - #define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() std::scoped_lock lk(this->mmc_device.device_mutex) - - #else - - #define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() - - #endif - - namespace { - - constexpr inline u32 OcrCardPowerUpStatus = (1 << 31); - - constexpr inline u32 OcrAccessMode_Mask = (3 << 29); - constexpr inline u32 OcrAccessMode_SectorMode = (2 << 29); - - constexpr inline u8 ManufacturerId_Toshiba = 0x11; - - enum DeviceType : u8 { - DeviceType_HighSpeed26MHz = (1u << 0), - DeviceType_HighSpeed52MHz = (1u << 1), - DeviceType_HighSpeedDdr52MHz1_8VOr3_0V = (1u << 2), - DeviceType_HighSpeedDdr52MHz1_2V = (1u << 3), - DeviceType_Hs200Sdr200MHz1_8V = (1u << 4), - DeviceType_Hs200Sdr200MHz1_2V = (1u << 5), - DeviceType_Hs400Sdr200MHz1_8V = (1u << 6), - DeviceType_Hs400Sdr200MHz1_2V = (1u << 7), - }; - - constexpr bool IsToshibaMmc(const u8 *cid) { - /* Check whether the CID's manufacturer id field is Toshiba. */ - AMS_ABORT_UNLESS(cid != nullptr); - return cid[14] == ManufacturerId_Toshiba; - } - - constexpr bool IsLessThanSpecification4(const u8 *csd) { - const u8 spec_vers = ((csd[14] >> 2) & 0xF); - return spec_vers < 4; - } - - constexpr bool IsBkopAutoEnable(const u8 *ext_csd) { - /* Check the AUTO_EN bit of BKOPS_EN. */ - return (ext_csd[163] & (1u << 1)) != 0; - } - - constexpr u8 GetDeviceType(const u8 *ext_csd) { - /* Get the DEVICE_TYPE register. */ - AMS_ABORT_UNLESS(ext_csd != nullptr); - return ext_csd[196]; - } - - constexpr bool IsSupportedHs400(u8 device_type) { - return (device_type & DeviceType_Hs400Sdr200MHz1_8V) != 0; - } - - constexpr bool IsSupportedHs200(u8 device_type) { - return (device_type & DeviceType_Hs200Sdr200MHz1_8V) != 0; - } - - constexpr bool IsSupportedHighSpeed(u8 device_type) { - return (device_type & DeviceType_HighSpeed52MHz) != 0; - } - - constexpr u32 GetMemoryCapacityFromExtCsd(const u32 *ext_csd) { - /* Get the SEC_COUNT register. */ - AMS_ABORT_UNLESS(ext_csd != nullptr); - return ext_csd[212 / sizeof(u32)]; - } - - constexpr u32 GetBootPartitionMemoryCapacityFromExtCsd(const u8 *ext_csd) { - /* Get the BOOT_SIZE_MULT register. */ - AMS_ABORT_UNLESS(ext_csd != nullptr); - return ext_csd[226] * (128_KB / SectorSize); - } - - constexpr Result GetCurrentSpeedModeFromExtCsd(SpeedMode *out, const u8 *ext_csd) { - /* Get the HS_TIMING register. */ - AMS_ABORT_UNLESS(out != nullptr); - AMS_ABORT_UNLESS(ext_csd != nullptr); - - switch (ext_csd[185] & 0xF) { - case 0: *out = SpeedMode_MmcLegacySpeed; break; - case 1: *out = SpeedMode_MmcHighSpeed; break; - case 2: *out = SpeedMode_MmcHs200; break; - case 3: *out = SpeedMode_MmcHs400; break; - default: return sdmmc::ResultUnexpectedMmcExtendedCsdValue(); - } - - return ResultSuccess(); - } - - } - - void MmcDevice::SetOcrAndHighCapacity(u32 ocr) { - /* Set ocr. */ - BaseDevice::SetOcr(ocr); - - /* Set high capacity. */ - BaseDevice::SetHighCapacity((ocr & OcrAccessMode_Mask) == OcrAccessMode_SectorMode); - } - - Result MmcDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const { - /* Get the command argument. */ - u32 arg = OcrAccessMode_SectorMode; - switch (bus_power) { - case BusPower_1_8V: arg |= 0x000080; break; - case BusPower_3_3V: arg |= 0x03F800; break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R3; - Command command(CommandIndex_SendOpCond, arg, CommandResponseType, false); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command))); - - /* Get the response. */ - hc->GetLastResponse(out_ocr, sizeof(*out_ocr), CommandResponseType); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandSetRelativeAddr() const { - /* Get rca. */ - const u32 rca = this->mmc_device.GetRca(); - AMS_ABORT_UNLESS(rca > 0); - - /* Issue comamnd. */ - const u32 arg = rca << 16; - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_SetRelativeAddr, arg, false, DeviceState_Unknown)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandSwitch(CommandSwitch cs) const { - /* Get the command argument. */ - const u32 arg = GetCommandSwitchArgument(cs); - - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Switch, arg, true, DeviceState_Unknown)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandSendExtCsd(void *dst, size_t dst_size) const { - /* Validate the output buffer. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= MmcExtendedCsdSize); - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(CommandIndex_SendExtCsd, 0, CommandResponseType, false); - TransferData xfer_data(dst, MmcExtendedCsdSize, 1, TransferDirection_ReadFromDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->mmc_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandEraseGroupStart(u32 sector_index) const { - /* Get the command argument. */ - const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize; - - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupStart, arg, false, DeviceState_Unknown)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandEraseGroupEnd(u32 sector_index) const { - /* Get the command argument. */ - const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize; - - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupEnd, arg, false, DeviceState_Tran)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::IssueCommandErase() const { - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Erase, 0, false, DeviceState_Tran)); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::CancelToshibaMmcModel() { - /* Special erase sequence done by Nintendo on Toshiba MMCs. */ - R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsProductionStateAwarenessEnable)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - R_TRY(this->IssueCommandSwitch(CommandSwitch_ClearBitsAutoModeEnable)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessNormal)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::ChangeToReadyState(BusPower bus_power) { - /* Be prepared to wait up to 1.5 seconds to change state. */ - ManualTimer timer(1500); - while (true) { - /* Get the ocr, and check if we're done. */ - u32 ocr; - R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), bus_power)); - if ((ocr & OcrCardPowerUpStatus) != 0) { - this->mmc_device.SetOcrAndHighCapacity(ocr); - return ResultSuccess(); - } - - /* Check if we've timed out. */ - R_UNLESS(timer.Update(), sdmmc::ResultMmcInitializationSoftwareTimeout()); - - /* Try again in 1ms. */ - WaitMicroSeconds(1000); - } - } - - Result MmcDeviceAccessor::ExtendBusWidth(BusWidth max_bw) { - /* If the maximum bus width is 1bit, we can't extend. */ - R_SUCCEED_IF(max_bw == BusWidth_1Bit); - - /* Determine what bus width to switch to. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - BusWidth target_bw = BusWidth_1Bit; - CommandSwitch cs; - if (max_bw == BusWidth_8Bit && hc->IsSupportedBusWidth(BusWidth_8Bit)) { - target_bw = BusWidth_8Bit; - cs = CommandSwitch_WriteBusWidth8Bit; - } else if ((max_bw == BusWidth_8Bit || max_bw == BusWidth_4Bit) && hc->IsSupportedBusWidth(BusWidth_4Bit)) { - target_bw = BusWidth_4Bit; - cs = CommandSwitch_WriteBusWidth4Bit; - } else { - /* Target bus width is 1bit. */ - return ResultSuccess(); - } - - /* Set the bus width. */ - R_TRY(this->IssueCommandSwitch(cs)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - hc->SetBusWidth(target_bw); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::EnableBkopsAuto() { - /* Issue the command. */ - R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsBkopsEnAutoEn)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::ChangeToHighSpeed(bool check_before) { - /* Issue high speed command. */ - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHighSpeed)); - - /* If we should check status before setting mode, do so. */ - if (check_before) { - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - } - - /* Set the host controller to high speed. */ - R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_MmcHighSpeed)); - - /* If we should check status after setting mode, do so. */ - if (!check_before) { - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - } - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::ChangeToHs200() { - /* Issue Hs200 command. */ - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs200)); - - /* Set the host controller to Hs200. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs200)); - - /* Perform tuning using command index 21. */ - R_TRY(hc->Tuning(SpeedMode_MmcHs200, 21)); - - /* Check status. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::ChangeToHs400() { - /* Change first to Hs200. */ - R_TRY(this->ChangeToHs200()); - - /* Save tuning status. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - hc->SaveTuningStatusForHs400(); - - /* Change to high speed. */ - R_TRY(this->ChangeToHighSpeed(false)); - - /* Issue Hs400 command. */ - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteBusWidth8BitDdr)); - R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs400)); - - /* Set the host controller to Hs400. */ - R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs400)); - - /* Check status. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::ExtendBusSpeed(u8 device_type, SpeedMode max_sm) { - /* We want to switch to the highest speed we can. */ - /* Check Hs400/Hs200 first. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - if (hc->IsSupportedTuning() && hc->GetBusPower() == BusPower_1_8V) { - if (hc->GetBusWidth() == BusWidth_8Bit && IsSupportedHs400(device_type) && max_sm == SpeedMode_MmcHs400) { - return this->ChangeToHs400(); - } else if ((hc->GetBusWidth() == BusWidth_8Bit || hc->GetBusWidth() == BusWidth_4Bit) && IsSupportedHs200(device_type) && (max_sm == SpeedMode_MmcHs400 || max_sm == SpeedMode_MmcHs200)) { - return this->ChangeToHs200(); - } - } - - /* Check if we can switch to high speed. */ - if (IsSupportedHighSpeed(device_type)) { - return this->ChangeToHighSpeed(true); - } - - /* We can't, so stay at normal speeds. */ - return ResultSuccess(); - } - - Result MmcDeviceAccessor::StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) { - /* Start up at an appropriate bus power. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - const BusPower bp = hc->IsSupportedBusPower(BusPower_1_8V) ? BusPower_1_8V : BusPower_3_3V; - R_TRY(hc->Startup(bp, BusWidth_1Bit, SpeedMode_MmcIdentification, false)); - - /* Wait 1ms for configuration to take. */ - WaitMicroSeconds(1000); - - /* Wait an additional 74 clocks for configuration to take. */ - WaitClocks(74, hc->GetDeviceClockFrequencyKHz()); - - /* Go to idle state. */ - R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState()); - this->current_partition = MmcPartition_UserData; - - /* Go to ready state. */ - R_TRY(this->ChangeToReadyState(bp)); - - /* Get the CID. */ - R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size)); - this->mmc_device.SetCid(wb, wb_size); - const bool is_toshiba = IsToshibaMmc(static_cast(wb)); - - /* Issue set relative addr. */ - R_TRY(this->IssueCommandSetRelativeAddr()); - - /* Get the CSD. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size)); - this->mmc_device.SetCsd(wb, wb_size); - const bool spec_under_4 = IsLessThanSpecification4(static_cast(wb)); - - /* Set the speed mode to legacy. */ - R_TRY(hc->SetSpeedMode(SpeedMode_MmcLegacySpeed)); - - /* Issue select card command. */ - R_TRY(BaseDeviceAccessor::IssueCommandSelectCard()); - - /* Set block length to sector size. */ - R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize()); - - /* If the device SPEC_VERS is less than 4, extended csd/switch aren't supported. */ - if (spec_under_4) { - R_TRY(this->mmc_device.SetLegacyMemoryCapacity()); - - this->mmc_device.SetActive(); - return ResultSuccess(); - } - - /* Extend the bus width to the largest that we can. */ - R_TRY(this->ExtendBusWidth(max_bw)); - - /* Get the extended csd. */ - R_TRY(this->IssueCommandSendExtCsd(wb, wb_size)); - AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(wb), alignof(u32))); - this->mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast(wb))); - - /* If the mmc is manufactured by toshiba, try to enable bkops auto. */ - if (is_toshiba && !IsBkopAutoEnable(static_cast(wb))) { - /* NOTE: Nintendo does not check the result of this. */ - this->EnableBkopsAuto(); - } - - /* Extend the bus speed to as fast as we can. */ - const u8 device_type = GetDeviceType(static_cast(wb)); - R_TRY(this->ExtendBusSpeed(device_type, max_sm)); - - /* Enable power saving. */ - hc->SetPowerSaving(true); - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::OnActivate() { - /* Define the possible startup parameters. */ - constexpr const struct { - BusWidth bus_width; - SpeedMode speed_mode; - } StartupParameters[] = { - #if defined(AMS_SDMMC_ENABLE_MMC_HS400) - { BusWidth_8Bit, SpeedMode_MmcHs400 }, - #else - { BusWidth_8Bit, SpeedMode_MmcHighSpeed }, - #endif - { BusWidth_8Bit, SpeedMode_MmcHighSpeed }, - { BusWidth_1Bit, SpeedMode_MmcHighSpeed }, - }; - - /* Try to start up with each set of parameters. */ - Result result; - for (int i = 0; i < static_cast(util::size(StartupParameters)); ++i) { - /* Alias the parameters. */ - const auto ¶ms = StartupParameters[i]; - - /* Set our max bus width/speed mode. */ - this->max_bus_width = params.bus_width; - this->max_speed_mode = params.speed_mode; - - /* Try to start up the device. */ - result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); - if (R_SUCCEEDED(result)) { - /* If we previously failed to start up the device, log the error correction. */ - if (i != 0) { - BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode); - BaseDeviceAccessor::IncrementNumActivationErrorCorrections(); - } - - return ResultSuccess(); - } - - /* Log that our startup failed. */ - BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); - - /* Shut down the host controller before we try to start up again. */ - BaseDeviceAccessor::GetHostController()->Shutdown(); - } - - /* We failed to start up with all sets of parameters. */ - BaseDeviceAccessor::PushErrorTimeStamp(); - - return result; - } - - Result MmcDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { - /* Get the sector index alignment. */ - u32 sector_index_alignment = 0; - if (!is_read) { - constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize; - sector_index_alignment = MmcWriteSectorAlignment; - AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment)); - } - - /* Do the read/write. */ - return BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, sector_index_alignment, buf, buf_size, is_read); - } - - Result MmcDeviceAccessor::ReStartup() { - /* Shut down the host controller. */ - BaseDeviceAccessor::GetHostController()->Shutdown(); - - /* Perform start up. */ - Result result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); - if (R_FAILED(result)) { - BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); - return result; - } - - return ResultSuccess(); - } - - void MmcDeviceAccessor::Initialize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* If we've already initialized, we don't need to do anything. */ - if (this->is_initialized) { - return; - } - - /* Set the base device to our mmc device. */ - BaseDeviceAccessor::SetDevice(std::addressof(this->mmc_device)); - - /* Initialize. */ - BaseDeviceAccessor::GetHostController()->Initialize(); - this->is_initialized = true; - } - - void MmcDeviceAccessor::Finalize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* If we've already finalized, we don't need to do anything. */ - if (!this->is_initialized) { - return; - } - this->is_initialized = false; - - /* Deactivate the device. */ - BaseDeviceAccessor::Deactivate(); - - /* Finalize the host controller. */ - BaseDeviceAccessor::GetHostController()->Finalize(); - } - - Result MmcDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { - /* Check that we can write to output. */ - AMS_ABORT_UNLESS(out_speed_mode != nullptr); - - /* Get the current speed mode from the ext csd. */ - R_TRY(GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size)); - R_TRY(GetCurrentSpeedModeFromExtCsd(out_speed_mode, static_cast(this->work_buffer))); - - return ResultSuccess(); - } - - void MmcDeviceAccessor::PutMmcToSleep() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* If the device isn't awake, we don't need to do anything. */ - if (!this->mmc_device.IsAwake()) { - return; - } - - /* Put the device to sleep. */ - this->mmc_device.PutToSleep(); - - /* If necessary, put the host controller to sleep. */ - if (this->mmc_device.IsActive()) { - BaseDeviceAccessor::GetHostController()->PutToSleep(); - } - } - - void MmcDeviceAccessor::AwakenMmc() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* If the device is awake, we don't need to do anything. */ - if (this->mmc_device.IsAwake()) { - return; - } - - /* Wake the host controller, if we need to.*/ - if (this->mmc_device.IsActive()) { - const Result result = BaseDeviceAccessor::GetHostController()->Awaken(); - if (R_FAILED(result)) { - BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue()); - } - } - - /* Wake the device. */ - this->mmc_device.Awaken(); - } - - Result MmcDeviceAccessor::SelectMmcPartition(MmcPartition part) { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* Check that we can access the device. */ - R_TRY(this->mmc_device.CheckAccessible()); - - /* Determine the appropriate SWITCH subcommand. */ - CommandSwitch cs; - switch (part) { - case MmcPartition_UserData: cs = CommandSwitch_WritePartitionAccessDefault; break; - case MmcPartition_BootPartition1: cs = CommandSwitch_WritePartitionAccessRwBootPartition1; break; - case MmcPartition_BootPartition2: cs = CommandSwitch_WritePartitionAccessRwBootPartition2; break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Change partition. */ - this->current_partition = MmcPartition_Unknown; - { - R_TRY(this->IssueCommandSwitch(cs)); - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - } - this->current_partition = part; - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::EraseMmc() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* Check that we can access the device. */ - R_TRY(this->mmc_device.CheckAccessible()); - - /* Get the partition capacity. */ - u32 part_capacity; - switch (this->current_partition) { - case MmcPartition_UserData: - part_capacity = this->mmc_device.GetMemoryCapacity(); - break; - case MmcPartition_BootPartition1: - case MmcPartition_BootPartition2: - R_TRY(this->GetMmcBootPartitionCapacity(std::addressof(part_capacity))); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Begin the erase. */ - R_TRY(this->IssueCommandEraseGroupStart(0)); - R_TRY(this->IssueCommandEraseGroupEnd(part_capacity - 1)); - - /* Issue the erase command, allowing 30 seconds for it to complete. */ - ManualTimer timer(30000); - Result result = this->IssueCommandErase(); - R_TRY_CATCH(result) { - R_CATCH(sdmmc::ResultDataTimeoutError) { /* Data timeout error is acceptable. */ } - R_CATCH(sdmmc::ResultCommandCompleteSoftwareTimeout) { /* Command complete software timeout error is acceptable. */ } - R_CATCH(sdmmc::ResultBusySoftwareTimeout) { /* Busy software timeout error is acceptable. */ } - } R_END_TRY_CATCH; - - /* Wait for the erase to finish. */ - while (true) { - /* Check if we're done. */ - result = BaseDeviceAccessor::IssueCommandSendStatus(); - if (R_SUCCEEDED(result)) { - break; - } - - /* Otherwise, check if we should reject the error. */ - if (!sdmmc::ResultUnexpectedDeviceState::Includes(result)) { - return result; - } - - /* Check if timeout has been exceeded. */ - R_UNLESS(timer.Update(), sdmmc::ResultMmcEraseSoftwareTimeout()); - } - - /* If the partition is user data, check if we need to perform toshiba-specific erase. */ - if (this->current_partition == MmcPartition_UserData) { - u8 cid[DeviceCidSize]; - this->mmc_device.GetCid(cid, sizeof(cid)); - if (IsToshibaMmc(cid)) { - /* NOTE: Nintendo does not check the result of this operation. */ - this->CancelToshibaMmcModel(); - } - } - - return ResultSuccess(); - } - - Result MmcDeviceAccessor::GetMmcBootPartitionCapacity(u32 *out_num_sectors) const { - /* Get the capacity from the extended csd. */ - AMS_ABORT_UNLESS(out_num_sectors != nullptr); - R_TRY(this->GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size)); - - *out_num_sectors = GetBootPartitionMemoryCapacityFromExtCsd(static_cast(this->work_buffer)); - return ResultSuccess(); - } - - Result MmcDeviceAccessor::GetMmcExtendedCsd(void *dst, size_t dst_size) const { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX(); - - /* Check that we can access the device. */ - R_TRY(this->mmc_device.CheckAccessible()); - - /* Get the csd. */ - u8 csd[DeviceCsdSize]; - this->mmc_device.GetCsd(csd, sizeof(csd)); - - /* Check that the card supports ext csd. */ - R_UNLESS(!IsLessThanSpecification4(csd), sdmmc::ResultMmcNotSupportExtendedCsd()); - - /* Get the ext csd. */ - R_TRY(this->IssueCommandSendExtCsd(dst, dst_size)); - - return ResultSuccess(); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp deleted file mode 100644 index 97ac864..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_base_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - class MmcDevice : public BaseDevice { - private: - static constexpr u16 Rca = 2; - public: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual os::EventType *GetRemovedEvent() const override { - /* Mmc can't be removed. */ - return nullptr; - } - #endif - - virtual DeviceType GetDeviceType() const override { - return DeviceType_Mmc; - } - - virtual u16 GetRca() const override { - return Rca; - } - - void SetOcrAndHighCapacity(u32 ocr); - }; - - class MmcDeviceAccessor : public BaseDeviceAccessor { - private: - MmcDevice mmc_device; - void *work_buffer; - size_t work_buffer_size; - BusWidth max_bus_width; - SpeedMode max_speed_mode; - MmcPartition current_partition; - bool is_initialized; - private: - enum CommandSwitch { - CommandSwitch_SetBitsProductionStateAwarenessEnable = 0, - CommandSwitch_ClearBitsAutoModeEnable = 1, - CommandSwitch_WriteProductionStateAwarenessNormal = 2, - CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites = 3, - CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites = 4, - CommandSwitch_SetBitsBkopsEnAutoEn = 5, - CommandSwitch_WriteBusWidth1Bit = 6, - CommandSwitch_WriteBusWidth4Bit = 7, - CommandSwitch_WriteBusWidth8Bit = 8, - CommandSwitch_WriteBusWidth8BitDdr = 9, - CommandSwitch_WriteHsTimingLegacySpeed = 10, - CommandSwitch_WriteHsTimingHighSpeed = 11, - CommandSwitch_WriteHsTimingHs200 = 12, - CommandSwitch_WriteHsTimingHs400 = 13, - CommandSwitch_WritePartitionAccessDefault = 14, - CommandSwitch_WritePartitionAccessRwBootPartition1 = 15, - CommandSwitch_WritePartitionAccessRwBootPartition2 = 16, - }; - - static constexpr ALWAYS_INLINE u32 GetCommandSwitchArgument(CommandSwitch cs) { - switch (cs) { - case CommandSwitch_SetBitsProductionStateAwarenessEnable: return 0x01111000; - case CommandSwitch_ClearBitsAutoModeEnable: return 0x02112000; - case CommandSwitch_WriteProductionStateAwarenessNormal: return 0x03850000; - case CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites: return 0x03850100; - case CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites: return 0x03850200; - case CommandSwitch_SetBitsBkopsEnAutoEn: return 0x01A30200; - case CommandSwitch_WriteBusWidth1Bit: return 0x03B70000; - case CommandSwitch_WriteBusWidth4Bit: return 0x03B70100; - case CommandSwitch_WriteBusWidth8Bit: return 0x03B70200; - case CommandSwitch_WriteBusWidth8BitDdr: return 0x03B70600; - case CommandSwitch_WriteHsTimingLegacySpeed: return 0x03B90000; - case CommandSwitch_WriteHsTimingHighSpeed: return 0x03B90100; - case CommandSwitch_WriteHsTimingHs200: return 0x03B90200; - case CommandSwitch_WriteHsTimingHs400: return 0x03B90300; - case CommandSwitch_WritePartitionAccessDefault: return 0x03B30000; - case CommandSwitch_WritePartitionAccessRwBootPartition1: return 0x03B30100; - case CommandSwitch_WritePartitionAccessRwBootPartition2: return 0x03B30200; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - private: - Result IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const; - Result IssueCommandSetRelativeAddr() const; - Result IssueCommandSwitch(CommandSwitch cs) const; - Result IssueCommandSendExtCsd(void *dst, size_t dst_size) const; - Result IssueCommandEraseGroupStart(u32 sector_index) const; - Result IssueCommandEraseGroupEnd(u32 sector_index) const; - Result IssueCommandErase() const; - Result CancelToshibaMmcModel(); - Result ChangeToReadyState(BusPower bus_power); - Result ExtendBusWidth(BusWidth max_bus_width); - Result EnableBkopsAuto(); - Result ChangeToHighSpeed(bool check_before); - Result ChangeToHs200(); - Result ChangeToHs400(); - Result ExtendBusSpeed(u8 device_type, SpeedMode max_sm); - Result StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size); - protected: - virtual Result OnActivate() override; - virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; - virtual Result ReStartup() override; - public: - virtual void Initialize() override; - virtual void Finalize() override; - virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; - public: - explicit MmcDeviceAccessor(IHostController *hc) - : BaseDeviceAccessor(hc), work_buffer(nullptr), work_buffer_size(0), - max_bus_width(BusWidth_8Bit), max_speed_mode(SpeedMode_MmcHs400), current_partition(MmcPartition_Unknown), - is_initialized(false) - { - /* ... */ - } - - void SetMmcWorkBuffer(void *wb, size_t wb_size) { - this->work_buffer = wb; - this->work_buffer_size = wb_size; - } - - void PutMmcToSleep(); - void AwakenMmc(); - Result SelectMmcPartition(MmcPartition part); - Result EraseMmc(); - Result GetMmcBootPartitionCapacity(u32 *out_num_sectors) const; - Result GetMmcExtendedCsd(void *dst, size_t dst_size) const; - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp deleted file mode 100644 index 763869d..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_port_gc_asic0.hpp" -#include "sdmmc_select_sdmmc_controller.hpp" - - -namespace ams::sdmmc::impl { - - namespace { - - SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller; - GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller)); - - } - - IHostController *GetHostControllerOfPortGcAsic0() { - return std::addressof(g_gc_asic0_host_controller); - } - - IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() { - return std::addressof(g_gc_asic0_device_accessor); - } - - GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() { - return std::addressof(g_gc_asic0_device_accessor); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp deleted file mode 100644 index 8dc7880..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" -#include "sdmmc_i_device_accessor.hpp" -#include "sdmmc_gc_asic_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - IHostController *GetHostControllerOfPortGcAsic0(); - IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0(); - GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0(); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp deleted file mode 100644 index 30cfc86..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_port_mmc0.hpp" -#include "sdmmc_select_sdmmc_controller.hpp" -#include "sdmmc_base_device_accessor.hpp" - - -namespace ams::sdmmc::impl { - - namespace { - - SdmmcControllerForPortMmc0 g_mmc0_host_controller; - MmcDeviceAccessor g_mmc0_device_accessor(std::addressof(g_mmc0_host_controller)); - - } - - IHostController *GetHostControllerOfPortMmc0() { - return std::addressof(g_mmc0_host_controller); - } - - IDeviceAccessor *GetDeviceAccessorOfPortMmc0() { - return std::addressof(g_mmc0_device_accessor); - } - - MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0() { - return std::addressof(g_mmc0_device_accessor); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp deleted file mode 100644 index b675bd4..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" -#include "sdmmc_i_device_accessor.hpp" -#include "sdmmc_mmc_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - IHostController *GetHostControllerOfPortMmc0(); - IDeviceAccessor *GetDeviceAccessorOfPortMmc0(); - MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0(); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp deleted file mode 100644 index 5288a5e..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_port_sd_card0.hpp" -#include "sdmmc_select_sdmmc_controller.hpp" - - -namespace ams::sdmmc::impl { - - namespace { - - SdmmcControllerForPortSdCard0 g_sd_card0_host_controller; - - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - - constexpr inline u32 SdCard0DebounceMilliSeconds = 128; - DeviceDetector g_sd_card0_detector(gpio::DeviceCode_SdCd, gpio::GpioValue_Low, SdCard0DebounceMilliSeconds); - - SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller), std::addressof(g_sd_card0_detector)); - - #else - - SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller)); - - #endif - - - } - - IHostController *GetHostControllerOfPortSdCard0() { - return std::addressof(g_sd_card0_host_controller); - } - - IDeviceAccessor *GetDeviceAccessorOfPortSdCard0() { - return std::addressof(g_sd_card0_device_accessor); - } - - SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0() { - return std::addressof(g_sd_card0_device_accessor); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp deleted file mode 100644 index 690dbb6..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" -#include "sdmmc_i_device_accessor.hpp" -#include "sdmmc_sd_card_device_accessor.hpp" - -namespace ams::sdmmc::impl { - - IHostController *GetHostControllerOfPortSdCard0(); - IDeviceAccessor *GetDeviceAccessorOfPortSdCard0(); - SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0(); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp deleted file mode 100644 index 9bfff1a..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_sd_card_device_accessor.hpp" -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl { - - #if defined(AMS_SDMMC_THREAD_SAFE) - - #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() std::scoped_lock lk(this->sd_card_device.device_mutex) - - #else - - #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() - - #endif - - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - - #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() R_UNLESS(!this->sd_card_device.IsRemoved(), sdmmc::ResultDeviceRemoved()) - - #else - - #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() - - #endif - - namespace { - - constexpr inline u32 OcrCardPowerUpStatus = (1 << 31); - constexpr inline u32 OcrCardCapacityStatus = (1 << 30); - constexpr inline u32 OcrSwitchingTo1_8VAccepted = (1 << 24); - - constexpr bool IsLessThanSpecification1_1(const u8 *scr) { - AMS_ABORT_UNLESS(scr != nullptr); - - const u8 sd_spec = scr[0] & 0xF; - return sd_spec < 1; - } - - constexpr u32 GetSendOpCmdArgument(bool spec_under_2, bool uhs_i_supported) { - const u32 hcs = !spec_under_2 ? (1u << 30) : (0u << 30); - const u32 xpc = !spec_under_2 ? (1u << 28) : (0u << 28); - const u32 s18r = (!spec_under_2 && uhs_i_supported) ? (1u << 24) : (0u << 24); - return hcs | xpc | s18r | 0x00100000u; - } - - constexpr bool IsLessThanCsdVersion2(const u8 *csd) { - AMS_ABORT_UNLESS(csd != nullptr); - - /* Check whether CSD_STRUCTURE is 0. */ - return ((csd[14] & 0xC0) >> 6) == 0; - } - - constexpr u32 GetMemoryCapacityFromCsd(const u16 *csd) { - AMS_ABORT_UNLESS(csd != nullptr); - - /* Get CSIZE, convert appropriately. */ - const u32 csize = (static_cast(csd[3] & 0x3FFF) << 8) | (static_cast(csd[2] & 0xFF00) >> 8); - return (1 + csize) << 10; - } - - constexpr u8 GetSdBusWidths(const u8 *scr) { - AMS_ABORT_UNLESS(scr != nullptr); - return scr[1] & 0xF; - } - - constexpr bool IsSupportedBusWidth4Bit(u8 sd_bw) { - return (sd_bw & 0x4) != 0; - } - - constexpr bool IsSupportedAccessMode(const u8 *status, SwitchFunctionAccessMode access_mode) { - AMS_ABORT_UNLESS(status != nullptr); - - return (status[13] & (1u << access_mode)) != 0; - } - - constexpr u8 GetAccessModeFromFunctionSelection(const u8 *status) { - AMS_ABORT_UNLESS(status != nullptr); - return (status[16] & 0xF); - } - - constexpr bool IsAccessModeInFunctionSelection(const u8 *status, SwitchFunctionAccessMode mode) { - return GetAccessModeFromFunctionSelection(status) == static_cast(mode); - } - - constexpr u16 GetMaximumCurrentConsumption(const u8 *status) { - AMS_ABORT_UNLESS(status != nullptr); - - return (static_cast(status[0]) << 8) | - (static_cast(status[1]) << 0); - } - - constexpr u32 GetSizeOfProtectedArea(const u8 *sd_status) { - return (static_cast(sd_status[4]) << 24) | - (static_cast(sd_status[5]) << 16) | - (static_cast(sd_status[6]) << 8) | - (static_cast(sd_status[7]) << 0); - } - - Result GetCurrentSpeedMode(SpeedMode *out_sm, const u8 *status, bool is_uhs_i) { - AMS_ABORT_UNLESS(out_sm != nullptr); - - /* Get the access mode. */ - switch (static_cast(GetAccessModeFromFunctionSelection(status))) { - case SwitchFunctionAccessMode_Default: - if (is_uhs_i) { - *out_sm = SpeedMode_SdCardSdr12; - } else { - *out_sm = SpeedMode_SdCardDefaultSpeed; - } - break; - case SwitchFunctionAccessMode_HighSpeed: - if (is_uhs_i) { - *out_sm = SpeedMode_SdCardSdr25; - } else { - *out_sm = SpeedMode_SdCardHighSpeed; - } - break; - case SwitchFunctionAccessMode_Sdr50: - *out_sm = SpeedMode_SdCardSdr50; - break; - case SwitchFunctionAccessMode_Sdr104: - *out_sm = SpeedMode_SdCardSdr104; - break; - case SwitchFunctionAccessMode_Ddr50: - *out_sm = SpeedMode_SdCardDdr50; - break; - default: - return sdmmc::ResultUnexpectedSdCardSwitchFunctionStatus(); - } - - return ResultSuccess(); - } - - } - - void SdCardDevice::SetOcrAndHighCapacity(u32 ocr) { - /* Set ocr. */ - BaseDevice::SetOcr(ocr); - - /* Set high capacity. */ - BaseDevice::SetHighCapacity((ocr & OcrCardCapacityStatus) != 0); - } - - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - void SdCardDeviceAccessor::RemovedCallback() { - /* Signal that the device was removed. */ - this->sd_card_device.SignalRemovedEvent(); - - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Shut down. */ - BaseDeviceAccessor::GetHostController()->Shutdown(); - } - #endif - - Result SdCardDeviceAccessor::IssueCommandSendRelativeAddr(u16 *out_rca) const { - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R6; - Command command(CommandIndex_SendRelativeAddr, 0, CommandResponseType, false); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - - /* Set the output rca. */ - AMS_ABORT_UNLESS(out_rca != nullptr); - *out_rca = static_cast(resp >> 16); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSendIfCond() const { - /* Get the argument. */ - constexpr u32 SendIfCommandArgument = 0x01AAu; - constexpr u32 SendIfCommandArgumentMask = 0x0FFFu; - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R7; - Command command(CommandIndex_SendIfCond, SendIfCommandArgument, CommandResponseType, false); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - - /* Verify that our argument was returned to us. */ - R_UNLESS((resp & SendIfCommandArgumentMask) == (SendIfCommandArgument & SendIfCommandArgumentMask), sdmmc::ResultSdCardValidationError()); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const { - /* Validate the output buffer. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); - - /* Get the argument. */ - constexpr u32 CheckSupportedFunctionArgument = 0x00FFFFFF; - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(CommandIndex_Switch, CheckSupportedFunctionArgument, CommandResponseType, false); - TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 1, TransferDirection_ReadFromDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const { - /* Validate the output buffer. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); - - /* Get the argument. */ - const u32 arg = (set_function ? (1u << 31) : (0u << 31)) | 0x00FFFFF0 | static_cast(access_mode); - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(CommandIndex_Switch, arg, CommandResponseType, false); - TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 1, TransferDirection_ReadFromDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandVoltageSwitch() const { - /* Issue the command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_VoltageSwitch, 0, false, DeviceState_Ready)); - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask) const { - /* Get arg. */ - const u32 arg = static_cast(this->sd_card_device.GetRca()) << 16; - - /* Issue the command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(CommandIndex_AppCmd, arg, CommandResponseType, false); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - - /* Mask out the ignored status bits. */ - if (ignore_mask != 0) { - resp &= ~ignore_mask; - } - - /* Check the device status. */ - R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); - - /* Check the app command bit. */ - R_UNLESS((resp & DeviceStatus_AppCmd) != 0, sdmmc::ResultUnexpectedSdCardAcmdDisabled()); - - /* Check the device state. */ - if (expected_state != DeviceState_Unknown) { - R_UNLESS(this->sd_card_device.GetDeviceState(resp) == expected_state, sdmmc::ResultUnexpectedDeviceState()); - } - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSetBusWidth4Bit() const { - /* Issue the application command. */ - constexpr u32 Arg = 0x2; - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetBusWidth, Arg, false, DeviceState_Tran)); - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSdStatus(void *dst, size_t dst_size) const { - /* Validate the output buffer. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= SdCardSdStatusSize); - - /* Issue the application command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(SdApplicationCommandIndex_SdStatus, 0, CommandResponseType, false); - TransferData xfer_data(dst, SdCardSdStatusSize, 1, TransferDirection_ReadFromDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const { - /* Get the argument. */ - const u32 arg = GetSendOpCmdArgument(spec_under_2, uhs_i_supported); - - /* Issue the application command. */ - constexpr ResponseType CommandResponseType = ResponseType_R3; - Command command(SdApplicationCommandIndex_SdSendOpCond, arg, CommandResponseType, false); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command))); - - /* Get the response. */ - hc->GetLastResponse(out_ocr, sizeof(u32), CommandResponseType); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandClearCardDetect() const { - /* Issue the application command. */ - R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetClearCardDetect, 0, false, DeviceState_Tran)); - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::IssueCommandSendScr(void *dst, size_t dst_size) const { - /* Validate the output buffer. */ - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(dst_size >= SdCardScrSize); - - /* Issue the application command. */ - constexpr ResponseType CommandResponseType = ResponseType_R1; - Command command(SdApplicationCommandIndex_SendScr, 0, CommandResponseType, false); - TransferData xfer_data(dst, SdCardScrSize, 1, TransferDirection_ReadFromDevice); - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data))); - - /* Get the response. */ - u32 resp; - hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); - R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::EnterUhsIMode() { - /* Send voltage switch command. */ - R_TRY(this->IssueCommandVoltageSwitch()); - - /* Switch to sdr12. */ - R_TRY(BaseDeviceAccessor::GetHostController()->SwitchToSdr12()); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::ChangeToReadyState(bool spec_under_2, bool uhs_i_supported) { - /* Decide on an ignore mask. */ - u32 ignore_mask = spec_under_2 ? static_cast(DeviceStatus_IllegalCommand) : 0u; - - /* Be prepared to wait up to 3.0 seconds to change state. */ - ManualTimer timer(3000); - while (true) { - /* We want to get ocr, which requires our sending an application command. */ - R_TRY(this->IssueCommandAppCmd(DeviceState_Unknown, ignore_mask)); - ignore_mask = 0; - - /* Get the ocr, and check if we're done. */ - u32 ocr; - R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), spec_under_2, uhs_i_supported)); - - if ((ocr & OcrCardPowerUpStatus) != 0) { - this->sd_card_device.SetOcrAndHighCapacity(ocr); - - /* Handle uhs i mode. */ - this->sd_card_device.SetUhsIMode(false); - if (uhs_i_supported && ((ocr & OcrSwitchingTo1_8VAccepted) != 0)) { - R_TRY(this->EnterUhsIMode()); - this->sd_card_device.SetUhsIMode(true); - } - - return ResultSuccess(); - } - - /* Check if we've timed out. */ - R_UNLESS(timer.Update(), sdmmc::ResultSdCardInitializationSoftwareTimeout()); - - /* Try again in 1ms. */ - WaitMicroSeconds(1000); - } - } - - Result SdCardDeviceAccessor::ChangeToStbyStateAndGetRca() { - /* Be prepared to wait up to 1.0 seconds to change state. */ - ManualTimer timer(1000); - while (true) { - /* Get rca. */ - u16 rca; - R_TRY(this->IssueCommandSendRelativeAddr(std::addressof(rca))); - if (rca != 0) { - this->sd_card_device.SetRca(rca); - return ResultSuccess(); - } - - /* Check if we've timed out. */ - R_UNLESS(timer.Update(), sdmmc::ResultSdCardGetValidRcaSoftwareTimeout()); - } - } - - Result SdCardDeviceAccessor::SetMemoryCapacity(const void *csd) { - if (IsLessThanCsdVersion2(static_cast(csd))) { - R_TRY(this->sd_card_device.SetLegacyMemoryCapacity()); - } else { - AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(csd), alignof(u16))); - this->sd_card_device.SetMemoryCapacity(GetMemoryCapacityFromCsd(static_cast(csd))); - } - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetScr(void *dst, size_t dst_size) const { - /* Issue the application command. */ - R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); - R_TRY(this->IssueCommandSendScr(dst, dst_size)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::ExtendBusWidth(BusWidth max_bw, u8 sd_bw) { - /* If the maximum bus width is 1bit, we can't extend. */ - R_SUCCEED_IF(max_bw == BusWidth_1Bit); - - /* If 4bit mode isn't supported, we can't extend. */ - R_SUCCEED_IF(!IsSupportedBusWidth4Bit(sd_bw)); - - /* If the host controller doesn't support 4bit mode, we can't extend. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_SUCCEED_IF(!hc->IsSupportedBusWidth(BusWidth_4Bit)); - - /* Issue the application command to change to 4bit mode. */ - R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); - R_TRY(this->IssueCommandSetBusWidth4Bit()); - - /* Set the host controller's bus width. */ - hc->SetBusWidth(BusWidth_4Bit); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size) { - /* Issue command to check if we can switch access mode. */ - R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, false, access_mode)); - R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardCannotSwitchAccessMode()); - - /* Check if we can accept the resulting current consumption. */ - constexpr u16 AcceptableCurrentLimit = 800; /* mA */ - R_UNLESS(GetMaximumCurrentConsumption(static_cast(wb)) < AcceptableCurrentLimit, sdmmc::ResultSdCardUnacceptableCurrentConsumption()); - - /* Switch the access mode. */ - R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, true, access_mode)); - R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardFailedSwitchAccessMode()); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size) { - /* Check that we're in 4bit bus mode. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_UNLESS(hc->GetBusWidth() == BusWidth_4Bit, sdmmc::ResultSdCardNot4BitBusWidthAtUhsIMode()); - - /* Determine what speed mode/access mode we should switch to. */ - R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); - SwitchFunctionAccessMode target_am; - SpeedMode target_sm; - if (max_sm == SpeedMode_SdCardSdr104 && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr104)) { - target_am = SwitchFunctionAccessMode_Sdr104; - target_sm = SpeedMode_SdCardSdr104; - } else if ((max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50) && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr50)) { - target_am = SwitchFunctionAccessMode_Sdr50; - target_sm = SpeedMode_SdCardSdr50; - } else { - return sdmmc::ResultSdCardNotSupportSdr104AndSdr50(); - } - - /* Switch the access mode. */ - R_TRY(this->SwitchAccessMode(target_am, wb, wb_size)); - - /* Set the host controller speed mode and perform tuning using command index 19. */ - R_TRY(hc->SetSpeedMode(target_sm)); - R_TRY(hc->Tuning(target_sm, 19)); - - /* Check status. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size) { - /* If the maximum speed is default speed, we have nothing to do. */ - R_SUCCEED_IF(max_sm == SpeedMode_SdCardDefaultSpeed); - - /* Otherwise, if the spec is under 1.1, we have nothing to do. */ - R_SUCCEED_IF(spec_under_1_1); - - /* Otherwise, Check if high speed is supported. */ - R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); - R_SUCCEED_IF(!IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_HighSpeed)); - - /* Switch the access mode. */ - R_TRY(this->SwitchAccessMode(SwitchFunctionAccessMode_HighSpeed, wb, wb_size)); - - /* Check status. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); - - /* Set the host controller speed mode. */ - R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_SdCardHighSpeed)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetSdStatus(void *dst, size_t dst_size) const { - /* Issue the application command. */ - R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); - R_TRY(this->IssueCommandSdStatus(dst, dst_size)); - - return ResultSuccess(); - } - - void SdCardDeviceAccessor::TryDisconnectDat3PullUpResistor() const { - /* Issue the application command to clear card detect. */ - /* NOTE: Nintendo accepts a failure. */ - if (R_SUCCEEDED(this->IssueCommandAppCmd(DeviceState_Tran))) { - /* NOTE: Nintendo does not check the result of this. */ - this->IssueCommandClearCardDetect(); - } - - /* NOTE: Nintendo does not check the result of this. */ - BaseDeviceAccessor::IssueCommandSendStatus(); - } - - Result SdCardDeviceAccessor::StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) { - /* Start up the host controller. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - R_TRY(hc->Startup(BusPower_3_3V, BusWidth_1Bit, SpeedMode_SdCardIdentification, false)); - - /* Wait 1ms for configuration to take. */ - WaitMicroSeconds(1000); - - /* Wait an additional 74 clocks for configuration to take. */ - WaitClocks(74, hc->GetDeviceClockFrequencyKHz()); - - /* Go to idle state. */ - R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState()); - - /* Check whether the spec is under 2.0. */ - bool spec_under_2 = false; - R_TRY_CATCH(this->IssueCommandSendIfCond()) { - R_CATCH(sdmmc::ResultResponseTimeoutError) { spec_under_2 = true; } - } R_END_TRY_CATCH; - - /* Set the rca to 0. */ - this->sd_card_device.SetRca(0); - - /* Go to ready state. */ - const bool can_use_uhs_i_mode = (max_bw != BusWidth_1Bit) && (max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50); - const bool uhs_i_supported = hc->IsSupportedTuning() && hc->IsSupportedBusPower(BusPower_1_8V); - R_TRY(this->ChangeToReadyState(spec_under_2, can_use_uhs_i_mode && uhs_i_supported)); - - /* Get the CID. */ - R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size)); - this->sd_card_device.SetCid(wb, wb_size); - - /* Go to stby state and get the RCA. */ - R_TRY(this->ChangeToStbyStateAndGetRca()); - - /* Get the CSD. */ - R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size)); - this->sd_card_device.SetCsd(wb, wb_size); - R_TRY(this->SetMemoryCapacity(wb)); - - /* Set the host controller speed mode to default if we're not in uhs i mode. */ - if (!this->sd_card_device.IsUhsIMode()) { - R_TRY(hc->SetSpeedMode(SpeedMode_SdCardDefaultSpeed)); - } - - /* Issue select card command. */ - R_TRY(BaseDeviceAccessor::IssueCommandSelectCard()); - - /* Set block length to sector size. */ - R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize()); - - /* Try to disconnect dat3 pullup resistor. */ - TryDisconnectDat3PullUpResistor(); - - /* Get the SCR. */ - R_TRY(this->GetScr(wb, wb_size)); - const u8 sd_bw = GetSdBusWidths(static_cast(wb)); - const bool spec_under_1_1 = IsLessThanSpecification1_1(static_cast(wb)); - - /* Extend the bus width to the largest that we can. */ - R_TRY(this->ExtendBusWidth(max_bw, sd_bw)); - - /* Extend the bus speed to as fast as we can. */ - if (this->sd_card_device.IsUhsIMode()) { - R_TRY(this->ExtendBusSpeedAtUhsIMode(max_sm, wb, wb_size)); - } else { - R_TRY(this->ExtendBusSpeedAtNonUhsIMode(max_sm, spec_under_1_1, wb, wb_size)); - } - - /* Enable power saving. */ - hc->SetPowerSaving(true); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::OnActivate() { - /* Define the possible startup parameters. */ - constexpr const struct { - BusWidth bus_width; - SpeedMode speed_mode; - } StartupParameters[] = { - #if defined(AMS_SDMMC_ENABLE_SD_UHS_I) - { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, - { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, - { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, - { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, - { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, - #else - { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, - { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, - { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, - { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, - #endif - }; - - /* Try to start up with each set of parameters. */ - Result result; - for (int i = 0; i < static_cast(util::size(StartupParameters)); ++i) { - /* Alias the parameters. */ - const auto ¶ms = StartupParameters[i]; - - /* Set our max bus width/speed mode. */ - this->max_bus_width = params.bus_width; - this->max_speed_mode = params.speed_mode; - - /* Try to start up the device. */ - result = this->StartupSdCardDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); - if (R_SUCCEEDED(result)) { - /* If we previously failed to start up the device, log the error correction. */ - if (i != 0) { - BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode); - BaseDeviceAccessor::IncrementNumActivationErrorCorrections(); - } - - return ResultSuccess(); - } - - /* Check if we were removed. */ - AMS_SDMMC_CHECK_SD_CARD_REMOVED(); - - /* Log that our startup failed. */ - BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); - - /* Shut down the host controller before we try to start up again. */ - BaseDeviceAccessor::GetHostController()->Shutdown(); - } - - /* We failed to start up with all sets of parameters. */ - /* Check the csd for errors. */ - if (sdmmc::ResultUnexpectedDeviceCsdValue::Includes(result)) { - u32 csd[DeviceCsdSize / sizeof(u32)]; - this->sd_card_device.GetCsd(csd, sizeof(csd)); - BaseDeviceAccessor::PushErrorLog(false, "%06X%08X%08X%08X", csd[3] & 0x00FFFFFF, csd[2], csd[1], csd[0]); - } - BaseDeviceAccessor::PushErrorTimeStamp(); - - /* Check if we failed because the sd card is removed. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { - WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); - AMS_SDMMC_CHECK_SD_CARD_REMOVED(); - } - #endif - - return result; - } - - Result SdCardDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { - /* Do the read/write. */ - const Result result = BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, 0, buf, buf_size, is_read); - - /* Check if we failed because the sd card is removed. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { - WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); - AMS_SDMMC_CHECK_SD_CARD_REMOVED(); - } - #endif - - return result; - } - - Result SdCardDeviceAccessor::ReStartup() { - /* Shut down the host controller. */ - BaseDeviceAccessor::GetHostController()->Shutdown(); - - /* Perform start up. */ - Result result = this->StartupSdCardDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); - if (R_FAILED(result)) { - AMS_SDMMC_CHECK_SD_CARD_REMOVED(); - - BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); - return result; - } - - return ResultSuccess(); - } - - void SdCardDeviceAccessor::Initialize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* If we've already initialized, we don't need to do anything. */ - if (this->is_initialized) { - return; - } - - /* Set the base device to our sd card device. */ - BaseDeviceAccessor::SetDevice(std::addressof(this->sd_card_device)); - - /* Initialize. */ - IHostController *hc = BaseDeviceAccessor::GetHostController(); - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - { - /* TODO: We probably want this (and other sd card detection stuff) to be conditional pcv control active. */ - /* This will be a requirement to support sd card access with detector in stratosphere before PCV is alive. */ - this->sd_card_device.InitializeRemovedEvent(); - hc->PreSetRemovedEvent(this->sd_card_device.GetRemovedEvent()); - CallbackInfo ci = { - .inserted_callback = nullptr, - .inserted_callback_arg = this, - .removed_callback = RemovedCallbackEntry, - .removed_callback_arg = this, - }; - this->sd_card_detector->Initialize(std::addressof(ci)); - } - #endif - hc->Initialize(); - - /* Mark ourselves as initialized. */ - this->is_initialized = true; - } - - void SdCardDeviceAccessor::Finalize() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* If we've already finalized, we don't need to do anything. */ - if (!this->is_initialized) { - return; - } - this->is_initialized = false; - - /* Deactivate the device. */ - BaseDeviceAccessor::Deactivate(); - - /* Finalize the host controller. */ - BaseDeviceAccessor::GetHostController()->Finalize(); - - /* Finalize the detector. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - { - this->sd_card_detector->Finalize(); - this->sd_card_device.FinalizeRemovedEvent(); - } - #endif - } - - Result SdCardDeviceAccessor::Activate() { - /* Activate the detector. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're awake. */ - R_UNLESS(this->sd_card_device.IsAwake(), sdmmc::ResultNotAwakened()); - - /* Check that we're not already active. */ - R_SUCCEED_IF(this->sd_card_device.IsActive()); - - /* Clear the removed event. */ - this->sd_card_device.ClearRemovedEvent(); - - /* Check that the SD card is inserted. */ - R_UNLESS(this->sd_card_detector->IsInserted(), sdmmc::ResultNoDevice()); - } - #endif - - /* Activate the base device. */ - return BaseDeviceAccessor::Activate(); - } - - Result SdCardDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const { - /* Check that we can write to output. */ - AMS_ABORT_UNLESS(out_speed_mode != nullptr); - - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->sd_card_device.CheckAccessible()); - - /* Check whether we're specification 1 (and thus default speed). */ - R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); - if (IsLessThanSpecification1_1(static_cast(this->work_buffer))) { - *out_speed_mode = SpeedMode_SdCardDefaultSpeed; - return ResultSuccess(); - } - - /* Get the current speed mode. */ - R_TRY(this->IssueCommandCheckSupportedFunction(this->work_buffer, this->work_buffer_size)); - R_TRY(GetCurrentSpeedMode(out_speed_mode, static_cast(this->work_buffer), this->sd_card_device.IsUhsIMode())); - - return ResultSuccess(); - } - - void SdCardDeviceAccessor::PutSdCardToSleep() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* If the device isn't awake, we don't need to do anything. */ - if (!this->sd_card_device.IsAwake()) { - return; - } - - /* Put the device to sleep. */ - this->sd_card_device.PutToSleep(); - - /* Put the detector to sleep. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - this->sd_card_detector->PutToSleep(); - #endif - - /* If necessary, put the host controller to sleep. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) - #else - if (this->sd_card_device.IsActive()) - #endif - { - BaseDeviceAccessor::GetHostController()->PutToSleep(); - } - } - - void SdCardDeviceAccessor::AwakenSdCard() { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* If the device is awake, we don't need to do anything. */ - if (this->sd_card_device.IsAwake()) { - return; - } - - /* Wake the host controller, if we need to.*/ - bool force_det = false; - - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) - #else - if (this->sd_card_device.IsActive()) - #endif - { - const Result result = BaseDeviceAccessor::GetHostController()->Awaken(); - if (R_SUCCEEDED(result)) { - force_det = R_FAILED(BaseDeviceAccessor::IssueCommandSendStatus()); - } else { - BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue()); - force_det = true; - } - } - - /* Wake the detector. */ - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - this->sd_card_detector->Awaken(force_det); - #else - AMS_UNUSED(force_det); - #endif - - /* Wake the device. */ - this->sd_card_device.Awaken(); - } - - Result SdCardDeviceAccessor::GetSdCardScr(void *dst, size_t dst_size) const { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->sd_card_device.CheckAccessible()); - - /* Get the SCR. */ - R_TRY(this->GetScr(dst, dst_size)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->sd_card_device.CheckAccessible()); - - /* Check whether we're specification 1 (and thus can't switch). */ - R_TRY(this->GetScr(dst, dst_size)); - R_UNLESS(!IsLessThanSpecification1_1(static_cast(dst)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); - - /* Get the status. */ - if (switch_function == SdCardSwitchFunction_CheckSupportedFunction) { - R_TRY(this->IssueCommandCheckSupportedFunction(dst, dst_size)); - } else { - SwitchFunctionAccessMode am; - switch (switch_function) { - case SdCardSwitchFunction_CheckDefault: am = SwitchFunctionAccessMode_Default; break; - case SdCardSwitchFunction_CheckHighSpeed: am = SwitchFunctionAccessMode_HighSpeed; break; - case SdCardSwitchFunction_CheckSdr50: am = SwitchFunctionAccessMode_Sdr50; break; - case SdCardSwitchFunction_CheckSdr104: am = SwitchFunctionAccessMode_Sdr104; break; - case SdCardSwitchFunction_CheckDdr50: am = SwitchFunctionAccessMode_Ddr50; break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - R_TRY(this->IssueCommandSwitchAccessMode(dst, dst_size, false, am)); - } - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->sd_card_device.CheckAccessible()); - - /* Check whether we're specification 1 (and thus can't switch). */ - R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); - R_UNLESS(!IsLessThanSpecification1_1(static_cast(this->work_buffer)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); - - /* Determine the access mode. */ - SwitchFunctionAccessMode am; - switch (speed_mode) { - case SpeedMode_SdCardSdr12: - case SpeedMode_SdCardDefaultSpeed: - am = SwitchFunctionAccessMode_Default; - break; - case SpeedMode_SdCardSdr25: - case SpeedMode_SdCardHighSpeed: - am = SwitchFunctionAccessMode_HighSpeed; - break; - case SpeedMode_SdCardSdr50: - am = SwitchFunctionAccessMode_Sdr50; - break; - case SpeedMode_SdCardSdr104: - am = SwitchFunctionAccessMode_Sdr104; - break; - case SpeedMode_SdCardDdr50: - am = SwitchFunctionAccessMode_Ddr50; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Check that the mode is supported. */ - R_TRY(this->IssueCommandSwitchAccessMode(this->work_buffer, this->work_buffer_size, false, am)); - R_UNLESS(IsSupportedAccessMode(static_cast(this->work_buffer), am), sdmmc::ResultSdCardNotSupportAccessMode()); - - /* Get the current consumption. */ - AMS_ABORT_UNLESS(out_current_consumption != nullptr); - *out_current_consumption = GetMaximumCurrentConsumption(static_cast(this->work_buffer)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetSdCardSdStatus(void *dst, size_t dst_size) const { - /* Acquire exclusive access to the device. */ - AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); - - /* Check that we're accessible. */ - R_TRY(this->sd_card_device.CheckAccessible()); - - /* Get the status. */ - R_TRY(this->GetSdStatus(dst, dst_size)); - - return ResultSuccess(); - } - - Result SdCardDeviceAccessor::GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const { - AMS_ABORT_UNLESS(out_num_sectors != nullptr); - - /* Get the sd status. */ - R_TRY(this->GetSdCardSdStatus(this->work_buffer, this->work_buffer_size)); - const u32 size_of_protected_area = GetSizeOfProtectedArea(static_cast(this->work_buffer)); - - /* Get the csd. */ - u8 csd[DeviceCsdSize]; - this->sd_card_device.GetCsd(csd, sizeof(csd)); - - /* Handle based on csd version. */ - if (IsLessThanCsdVersion2(csd)) { - /* Get c_size_mult and read_bl_len. */ - u8 c_size_mult, read_bl_len; - this->sd_card_device.GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len)); - - /* Validate the parameters. */ - R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue()); - - /* Calculate capacity. */ - *out_num_sectors = size_of_protected_area << ((read_bl_len + c_size_mult + 2) - 9); - } else { - /* SIZE_OF_PROTECTED_AREA is in bytes. */ - *out_num_sectors = size_of_protected_area / SectorSize; - } - - return ResultSuccess(); - } - - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp deleted file mode 100644 index 4f37b0c..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_base_device_accessor.hpp" - -#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) -#include "sdmmc_device_detector.hpp" -#endif - -namespace ams::sdmmc::impl { - - class SdCardDevice : public BaseDevice { - private: - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - mutable os::EventType removed_event; - #endif - u16 rca; - bool is_valid_rca; - bool is_uhs_i_mode; - public: - SdCardDevice() : rca(0) { - this->OnDeactivate(); - } - - virtual void Deactivate() override { - this->OnDeactivate(); - BaseDevice::Deactivate(); - } - - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - virtual os::EventType *GetRemovedEvent() const override { - return std::addressof(this->removed_event); - } - #elif defined(AMS_SDMMC_USE_OS_EVENTS) - virtual os::EventType *GetRemovedEvent() const override { - /* Mmc can't be removed. */ - return nullptr; - } - #endif - - virtual DeviceType GetDeviceType() const override { - return DeviceType_SdCard; - } - - virtual u16 GetRca() const override { - AMS_ABORT_UNLESS(this->is_valid_rca); - return this->rca; - } - - void OnDeactivate() { - this->is_valid_rca = false; - this->is_uhs_i_mode = false; - } - - void SetRca(u16 v) { - this->rca = v; - this->is_valid_rca = true; - } - - void SetOcrAndHighCapacity(u32 ocr); - - void SetUhsIMode(bool en) { - this->is_uhs_i_mode = en; - } - - bool IsUhsIMode() const { - return this->is_uhs_i_mode; - } - }; - - enum SdCardApplicationCommandIndex : std::underlying_type::type { - SdApplicationCommandIndex_SetBusWidth = 6, - - SdApplicationCommandIndex_SdStatus = 13, - - SdApplicationCommandIndex_SendNumWriteBlocks = 22, - SdApplicationCommandIndex_SetWriteBlockEraseCount = 23, - - SdApplicationCommandIndex_SdSendOpCond = 41, - SdApplicationCommandIndex_SetClearCardDetect = 42, - - SdApplicationCommandIndex_SendScr = 51, - }; - - enum SwitchFunctionAccessMode { - SwitchFunctionAccessMode_Default = 0, - SwitchFunctionAccessMode_HighSpeed = 1, - SwitchFunctionAccessMode_Sdr50 = 2, - SwitchFunctionAccessMode_Sdr104 = 3, - SwitchFunctionAccessMode_Ddr50 = 4, - }; - - class SdCardDeviceAccessor : public BaseDeviceAccessor { - private: - SdCardDevice sd_card_device; - void *work_buffer; - size_t work_buffer_size; - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - DeviceDetector *sd_card_detector; - #endif - BusWidth max_bus_width; - SpeedMode max_speed_mode; - bool is_initialized; - private: - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - void RemovedCallback(); - - static void RemovedCallbackEntry(void *arg) { - static_cast(arg)->RemovedCallback(); - } - #endif - - Result IssueCommandSendRelativeAddr(u16 *out_rca) const; - Result IssueCommandSendIfCond() const; - Result IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const; - Result IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const; - Result IssueCommandVoltageSwitch() const; - Result IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask = 0) const; - Result IssueCommandSetBusWidth4Bit() const; - Result IssueCommandSdStatus(void *dst, size_t dst_size) const; - Result IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const; - Result IssueCommandClearCardDetect() const; - Result IssueCommandSendScr(void *dst, size_t dst_size) const; - - Result EnterUhsIMode(); - Result ChangeToReadyState(bool spec_under_2, bool uhs_i_supported); - Result ChangeToStbyStateAndGetRca(); - Result SetMemoryCapacity(const void *csd); - Result GetScr(void *dst, size_t dst_size) const; - Result ExtendBusWidth(BusWidth max_bw, u8 sd_bw); - Result SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size); - Result ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size); - Result ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size); - Result GetSdStatus(void *dst, size_t dst_size) const; - Result StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size); - - void TryDisconnectDat3PullUpResistor() const; - protected: - virtual Result OnActivate() override; - virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override; - virtual Result ReStartup() override; - public: - virtual void Initialize() override; - virtual void Finalize() override; - virtual Result Activate() override; - virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; - public: - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - explicit SdCardDeviceAccessor(IHostController *hc, DeviceDetector *dd) : BaseDeviceAccessor(hc), sd_card_detector(dd) - #else - explicit SdCardDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc) - #endif - { - this->work_buffer = nullptr; - this->work_buffer_size = 0; - this->max_bus_width = BusWidth_4Bit; - this->max_speed_mode = SpeedMode_SdCardSdr104; - this->is_initialized = false; - } - - void SetSdCardWorkBuffer(void *wb, size_t wb_size) { - this->work_buffer = wb; - this->work_buffer_size = wb_size; - } - - void PutSdCardToSleep(); - void AwakenSdCard(); - Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const; - Result GetSdCardScr(void *dst, size_t dst_size) const; - Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const; - Result GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const; - Result GetSdCardSdStatus(void *dst, size_t dst_size) const; - - bool IsSdCardInserted() { - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - return this->sd_card_detector->IsInserted(); - #else - AMS_ABORT("IsSdCardInserted without SdCardDetector"); - #endif - } - - bool IsSdCardRemoved() { - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - return this->sd_card_device.IsRemoved(); - #else - AMS_ABORT("IsSdCardRemoved without SdCardDetector"); - #endif - } - - void RegisterSdCardDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) { - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - return this->sd_card_detector->RegisterDetectionEventCallback(cb, arg); - #else - AMS_UNUSED(cb, arg); - AMS_ABORT("RegisterSdCardDetectionEventCallback without SdCardDetector"); - #endif - } - - void UnregisterSdCardDetectionEventCallback() { - #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) - return this->sd_card_detector->UnregisterDetectionEventCallback(); - #else - AMS_ABORT("UnregisterSdCardDetectionEventCallback without SdCardDetector"); - #endif - } - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp deleted file mode 100644 index f03d153..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp +++ /dev/null @@ -1,1039 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_sd_host_standard_controller.hpp" -#include "sdmmc_timer.hpp" - -#if defined(ATMOSPHERE_IS_STRATOSPHERE) - #include -#endif - - -namespace ams::sdmmc::impl { - - namespace { - - constexpr inline u32 ControllerReactionTimeoutMilliSeconds = 2000; - constexpr inline u32 CommandTimeoutMilliSeconds = 2000; - constexpr inline u32 DefaultCheckTransferIntervalMilliSeconds = 1500; - constexpr inline u32 BusyTimeoutMilliSeconds = 2000; - - } - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - void SdHostStandardController::ResetBufferInfos() { - for (auto &info : this->buffer_infos) { - info.buffer_address = 0; - info.buffer_size = 0; - } - } - - dd::DeviceVirtualAddress SdHostStandardController::GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size) { - /* Try to find the buffer in our registered regions. */ - dd::DeviceVirtualAddress device_addr = 0; - for (const auto &info : this->buffer_infos) { - if (info.buffer_address <= buffer && (buffer + buffer_size) <= (info.buffer_address + info.buffer_size)) { - device_addr = info.buffer_device_virtual_address + (buffer - info.buffer_address); - break; - } - } - - /* Ensure that we found the buffer. */ - AMS_ABORT_UNLESS(device_addr != 0); - return device_addr; - } - #endif - - void SdHostStandardController::EnsureControl() { - /* Perform a read of clock control to be sure previous configuration takes. */ - reg::Read(this->registers->clock_control); - } - - Result SdHostStandardController::EnableInternalClock() { - /* Enable internal clock. */ - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, OSCILLATE)); - this->EnsureControl(); - - /* Wait for the internal clock to become stable. */ - { - ManualTimer timer(ControllerReactionTimeoutMilliSeconds); - while (true) { - /* Check if the clock is steady. */ - if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, READY))) { - break; - } - - /* If not, check for timeout. */ - R_UNLESS(timer.Update(), sdmmc::ResultInternalClockStableSoftwareTimeout()); - } - } - - /* Configure to use host controlled divided clock. */ - reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, HOST_DRIVER)); - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, DIVIDED_CLOCK)); - - /* Set host version 4.0.0 enable. */ - reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, VERSION_4)); - - /* Set host 64 bit addressing enable. */ - AMS_ABORT_UNLESS(reg::HasValue(this->registers->capabilities, SD_REG_BITS_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, SUPPORTED))); - reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 64_BIT_ADDRESSING)); - - /* Select SDMA mode. */ - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DMA_SELECT, SDMA)); - - /* Configure timeout control to use the maximum timeout value (TMCLK * 2^27) */ - reg::ReadWrite(this->registers->timeout_control, SD_REG_BITS_VALUE(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0b1110)); - - return ResultSuccess(); - } - - void SdHostStandardController::SetBusPower(BusPower bus_power) { - /* Check that we support the bus power. */ - AMS_ABORT_UNLESS(this->IsSupportedBusPower(bus_power)); - - /* Set the appropriate power. */ - switch (bus_power) { - case BusPower_Off: - reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, OFF)); - break; - case BusPower_1_8V: - reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1_8V)); - reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); - break; - case BusPower_3_3V: - reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 3_3V)); - reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void SdHostStandardController::EnableInterruptStatus() { - /* Set the status register interrupt enables. */ - reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); - reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); - - /* Read/write the interrupt enables to be sure they take. */ - reg::Write(this->registers->normal_int_enable, reg::Read(this->registers->normal_int_enable)); - reg::Write(this->registers->error_int_enable, reg::Read(this->registers->error_int_enable)); - - /* If we're using interrupt events, configure appropriately. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - /* Clear interrupts. */ - this->ClearInterrupt(); - - /* Enable the interrupt signals. */ - reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); - reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); - } - #endif - } - - void SdHostStandardController::DisableInterruptStatus() { - /* If we're using interrupt events, configure appropriately. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - /* Disable the interrupt signals. */ - reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); - reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); - } - #endif - - /* Mask the status register interrupt enables. */ - reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); - reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); - } - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - Result SdHostStandardController::WaitInterrupt(u32 timeout_ms) { - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Wait for the interrupt to be signaled. */ - os::WaitableHolderType *signaled_holder = os::TimedWaitAny(std::addressof(this->waitable_manager), TimeSpan::FromMilliSeconds(timeout_ms)); - if (signaled_holder == std::addressof(this->interrupt_event_holder)) { - /* We received the interrupt. */ - return ResultSuccess(); - } else if (signaled_holder == std::addressof(this->removed_event_holder)) { - /* The device was removed. */ - return sdmmc::ResultDeviceRemoved(); - } else { - /* Timeout occurred. */ - return sdmmc::ResultWaitInterruptSoftwareTimeout(); - } - } - - void SdHostStandardController::ClearInterrupt() { - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Clear the interrupt event. */ - os::ClearInterruptEvent(this->interrupt_event); - } - #endif - - void SdHostStandardController::SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data) { - /* Ensure the transfer data is valid. */ - AMS_ABORT_UNLESS(xfer_data->block_size != 0); - AMS_ABORT_UNLESS(xfer_data->num_blocks != 0); - - /* Determine the number of blocks. */ - const u16 num_blocks = std::min(xfer_data->num_blocks, SdHostStandardRegisters::BlockCountMax); - - /* Determine the address/how many blocks to transfer. */ - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - const u64 address = this->GetDeviceVirtualAddress(reinterpret_cast(xfer_data->buffer), xfer_data->block_size * num_blocks); - const u16 num_xfer_blocks = num_blocks; - #else - const u64 address = reinterpret_cast(xfer_data->buffer); - const u16 num_xfer_blocks = num_blocks; - #endif - - /* Verify the address is usable. */ - AMS_ABORT_UNLESS(util::IsAligned(address, BufferDeviceVirtualAddressAlignment)); - - /* Configure for sdma. */ - reg::Write(this->registers->adma_address, static_cast(address >> 0)); - reg::Write(this->registers->upper_adma_address, static_cast(address >> BITSIZEOF(u32))); - - /* Set our next sdma address. */ - this->next_sdma_address = util::AlignDown(address + SdmaBufferBoundary, SdmaBufferBoundary); - - /* Configure block size. */ - AMS_ABORT_UNLESS(xfer_data->block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); - reg::Write(this->registers->block_size, SD_REG_BITS_ENUM (BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 512_KB), - SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, static_cast(xfer_data->block_size))); - - /* Configure transfer blocks. */ - reg::Write(this->registers->block_count, num_xfer_blocks); - if (out_num_transferred_blocks != nullptr) { - *out_num_transferred_blocks = num_xfer_blocks; - } - - /* Configure transfer mode. */ - reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM (TRANSFER_MODE_DMA_ENABLE, ENABLE), - SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_BLOCK_COUNT_ENABLE, (xfer_data->is_multi_block_transfer), ENABLE, DISABLE), - SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_MULTI_BLOCK_SELECT, (xfer_data->is_multi_block_transfer), MULTI_BLOCK, SINGLE_BLOCK), - SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, (xfer_data->transfer_direction == TransferDirection_ReadFromDevice), READ, WRITE), - SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_AUTO_CMD_ENABLE, (xfer_data->is_stop_transmission_command_enabled), CMD12_ENABLE, DISABLE)); - } - - void SdHostStandardController::SetTransferForTuning() { - /* Get the tuning block size. */ - u16 tuning_block_size; - switch (this->GetBusWidth()) { - case BusWidth_4Bit: - tuning_block_size = 64; - break; - case BusWidth_8Bit: - tuning_block_size = 128; - break; - case BusWidth_1Bit: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Configure block size. */ - AMS_ABORT_UNLESS(tuning_block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); - reg::Write(this->registers->block_size, SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, tuning_block_size)); - - /* Configure transfer blocks. */ - reg::Write(this->registers->block_count, 1); - - /* Configure transfer mode. */ - reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, READ)); - } - - void SdHostStandardController::SetCommand(const Command *command, bool has_xfer_data) { - /* Encode the command value. */ - u16 command_val = 0; - - /* Encode the response type. */ - switch (command->response_type) { - case ResponseType_R0: - command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, NO_RESPONSE), - SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), - SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); - break; - case ResponseType_R1: - case ResponseType_R6: - case ResponseType_R7: - command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_RESPONSE_TYPE, command->is_busy, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE, RESPONSE_LENGTH_48), - SD_REG_BITS_ENUM (COMMAND_CRC_CHECK, ENABLE), - SD_REG_BITS_ENUM (COMMAND_INDEX_CHECK, ENABLE)); - break; - case ResponseType_R2: - command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_136), - SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, ENABLE), - SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); - break; - case ResponseType_R3: - command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_48), - SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), - SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Encode the data present select. */ - command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_DATA_PRESENT, has_xfer_data, DATA_PRESENT, NO_DATA_PRESENT)); - - /* Encode the command index. */ - AMS_ABORT_UNLESS(command->command_index <= SdHostStandardCommandIndexMax); - command_val |= reg::Encode(SD_REG_BITS_VALUE(COMMAND_COMMAND_INDEX, command->command_index)); - - /* Write the command and argument. */ - reg::Write(this->registers->argument, command->command_argument); - reg::Write(this->registers->command, command_val); - } - - void SdHostStandardController::SetCommandForTuning(u32 command_index) { - Command command(command_index, 0, ResponseType_R1, false); - return this->SetCommand(std::addressof(command), true); - } - - Result SdHostStandardController::ResetCmdDatLine() { - /* Set the software reset cmd/dat bits. */ - reg::ReadWrite(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, RESET), - SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, RESET)); - - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Wait until reset is done. */ - { - ManualTimer timer(ControllerReactionTimeoutMilliSeconds); - while (true) { - /* Check if the target has been removed. */ - R_TRY(this->CheckRemoved()); - - /* Check if command inhibit is no longer present. */ - if (reg::HasValue(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, WORK), - SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, WORK))) - { - break; - } - - /* Otherwise, check if we've timed out. */ - if (!timer.Update()) { - return sdmmc::ResultAbortTransactionSoftwareTimeout(); - } - } - } - - return ResultSuccess(); - } - - Result SdHostStandardController::AbortTransaction() { - R_TRY(this->ResetCmdDatLine()); - return ResultSuccess(); - } - - Result SdHostStandardController::WaitWhileCommandInhibit(bool has_dat) { - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Wait while command inhibit cmd is set. */ - { - ManualTimer timer(ControllerReactionTimeoutMilliSeconds); - while (true) { - /* Check if the target has been removed. */ - R_TRY(this->CheckRemoved()); - - /* Check if command inhibit is no longer present. */ - if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, READY))) { - break; - } - - /* Otherwise, check if we've timed out. */ - if (!timer.Update()) { - this->AbortTransaction(); - return sdmmc::ResultCommandInhibitCmdSoftwareTimeout(); - } - } - } - - /* Wait while command inhibit dat is set. */ - if (has_dat) { - ManualTimer timer(ControllerReactionTimeoutMilliSeconds); - while (true) { - /* Check if the target has been removed. */ - R_TRY(this->CheckRemoved()); - - /* Check if command inhibit is no longer present. */ - if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, READY))) { - break; - } - - /* Otherwise, check if we've timed out. */ - if (!timer.Update()) { - this->AbortTransaction(); - return sdmmc::ResultCommandInhibitDatSoftwareTimeout(); - } - } - } - - return ResultSuccess(); - } - - Result SdHostStandardController::CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask) { - /* Read the statuses. */ - volatile u16 normal_int_status = reg::Read(this->registers->normal_int_status); - volatile u16 error_int_status = reg::Read(this->registers->error_int_status); - volatile u16 auto_cmd_err_status = reg::Read(this->registers->acmd12_err); - - /* Set the output status, if necessary. */ - if (out_normal_int_status != nullptr) { - *out_normal_int_status = normal_int_status; - } - - /* If we don't have an error interrupt, just use the normal status. */ - if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, NO_ERROR))) { - /* If the wait mask has any bits set, we're done waiting for the interrupt. */ - const u16 masked_status = (normal_int_status & wait_mask); - R_UNLESS(masked_status != 0, sdmmc::ResultNoWaitedInterrupt()); - - /* Write the masked value to the status register to ensure consistent state. */ - reg::Write(this->registers->normal_int_status, masked_status); - return ResultSuccess(); - } - - /* We have an error interrupt. Write the status to the register to ensure consistent state. */ - reg::Write(this->registers->error_int_status, error_int_status); - - /* Check the error interrupt status bits, and return appropriate errors. */ - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, NO_ERROR)), sdmmc::ResultResponseIndexError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, NO_ERROR)), sdmmc::ResultResponseEndBitError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, NO_ERROR)), sdmmc::ResultResponseCrcError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, NO_ERROR)), sdmmc::ResultResponseTimeoutError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, NO_ERROR)), sdmmc::ResultDataEndBitError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, NO_ERROR)), sdmmc::ResultDataCrcError()); - R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, NO_ERROR)), sdmmc::ResultDataTimeoutError()); - - /* Check for auto cmd errors. */ - if (reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, ERROR))) { - R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, NO_ERROR)), sdmmc::ResultAutoCommandResponseIndexError()); - R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, NO_ERROR)), sdmmc::ResultAutoCommandResponseEndBitError()); - R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, NO_ERROR)), sdmmc::ResultAutoCommandResponseCrcError()); - R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, NO_ERROR)), sdmmc::ResultAutoCommandResponseTimeoutError()); - - /* An known auto cmd error occurred. */ - return sdmmc::ResultSdHostStandardUnknownAutoCmdError(); - } else { - /* Unknown error occurred. */ - return sdmmc::ResultSdHostStandardUnknownError(); - } - } - - Result SdHostStandardController::WaitCommandComplete() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - /* Wait for interrupt. */ - Result result = this->WaitInterrupt(CommandTimeoutMilliSeconds); - if (R_SUCCEEDED(result)) { - /* If we succeeded, check/clear our interrupt status. */ - result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); - this->ClearInterrupt(); - - if (R_FAILED(result)) { - this->AbortTransaction(); - } - - return result; - } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { - /* Otherwise, check if the device was removed. */ - return result; - } else { - /* If the device wasn't removed, cancel our transaction. */ - this->AbortTransaction(); - return sdmmc::ResultCommandCompleteSoftwareTimeout(); - } - } - #else - { - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Wait while command is not complete. */ - { - ManualTimer timer(CommandTimeoutMilliSeconds); - while (true) { - /* Check and clear the interrupt status. */ - const auto result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); - - /* If we succeeded, we're done. */ - if (R_SUCCEEDED(result)) { - return ResultSuccess(); - } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { - /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ - if (!timer.Update()) { - this->AbortTransaction(); - return sdmmc::ResultCommandCompleteSoftwareTimeout(); - } - } else { - /* Otherwise, we have a generic failure. */ - this->AbortTransaction(); - return result; - } - } - } - } - #endif - } - - Result SdHostStandardController::WaitTransferComplete() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - /* Wait while transfer is not complete. */ - while (true) { - /* Get the last block count. */ - const u16 last_block_count = reg::Read(this->registers->block_count); - - /* Wait for interrupt. */ - Result result = this->WaitInterrupt(this->check_transfer_interval_ms); - if (R_SUCCEEDED(result)) { - /* If we succeeded, check/clear our interrupt status. */ - volatile u16 normal_int_status; - result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); - - this->ClearInterrupt(); - - /* If the interrupt succeeded, check status. */ - if (R_SUCCEEDED(result)) { - /* If the transfer is complete, we're done. */ - if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { - return ResultSuccess(); - } - - /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ - if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { - reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); - reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); - - this->next_sdma_address += SdmaBufferBoundary; - } - } else { - /* Abort the transaction. */ - this->AbortTransaction(); - return result; - } - - return result; - } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { - /* Otherwise, check if the device was removed. */ - return result; - } else { - /* Otherwise, timeout if the transfer hasn't advanced. */ - if (last_block_count != reg::Read(this->registers->block_count)) { - this->AbortTransaction(); - return sdmmc::ResultTransferCompleteSoftwareTimeout(); - } - } - } - } - #else - { - /* Wait while transfer is not complete. */ - while (true) { - /* Get the last block count. */ - const u16 last_block_count = reg::Read(this->registers->block_count); - - /* Wait until transfer times out. */ - { - ManualTimer timer(this->check_transfer_interval_ms); - while (true) { - /* Check/clear our interrupt status. */ - volatile u16 normal_int_status; - const auto result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); - - /* If the check succeeded, check status. */ - if (R_SUCCEEDED(result)) { - /* If the transfer is complete, we're done. */ - if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { - return ResultSuccess(); - } - - /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ - if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { - reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); - reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); - - this->next_sdma_address += SdmaBufferBoundary; - } - } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { - /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ - if (!timer.Update()) { - /* Only timeout if the transfer hasn't advanced. */ - if (last_block_count != reg::Read(this->registers->block_count)) { - this->AbortTransaction(); - return sdmmc::ResultTransferCompleteSoftwareTimeout(); - } - break; - } - } else { - /* Otherwise, we have a generic failure. */ - this->AbortTransaction(); - return result; - } - } - } - } - } - #endif - } - - Result SdHostStandardController::WaitWhileBusy() { - /* Ensure that we control the registers. */ - this->EnsureControl(); - - /* Wait while busy. */ - { - ManualTimer timer(BusyTimeoutMilliSeconds); - while (true) { - /* Check if the target has been removed. */ - R_TRY(this->CheckRemoved()); - - /* If the DAT0 line signal is level high, we're done. */ - if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, HIGH))) { - return ResultSuccess(); - } - - /* Otherwise, check if we're timed out. */ - if (!timer.Update()) { - this->AbortTransaction(); - return sdmmc::ResultBusySoftwareTimeout(); - } - } - } - } - - void SdHostStandardController::GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { - /* Check that we can write the response. */ - AMS_ABORT_UNLESS(out_response != nullptr); - - /* Get the response appropriately. */ - switch (response_type) { - case ResponseType_R1: - case ResponseType_R3: - case ResponseType_R6: - case ResponseType_R7: - /* 32-bit response. */ - AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); - out_response[0] = reg::Read(this->registers->response[0]); - break; - case ResponseType_R2: - /* 128-bit response. */ - AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); - out_response[0] = reg::Read(this->registers->response[0]); - out_response[1] = reg::Read(this->registers->response[1]); - out_response[2] = reg::Read(this->registers->response[2]); - out_response[3] = reg::Read(this->registers->response[3]); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - Result SdHostStandardController::IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { - /* Wait until we can issue the command. */ - R_TRY(this->WaitWhileCommandInhibit((xfer_data != nullptr) || command->is_busy)); - - /* Configure for the transfer. */ - u32 num_transferred_blocks = 0; - if (xfer_data != nullptr) { - /* Setup the transfer, and get the number of blocks. */ - this->SetTransfer(std::addressof(num_transferred_blocks), xfer_data); - - /* Ensure the device sees consistent data with the cpu. */ - dd::FlushDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); - } - - /* Issue the command with interrupt status enabled. */ - { - this->EnableInterruptStatus(); - ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; - - /* Set the command. */ - this->SetCommand(command, xfer_data != nullptr); - - /* Wait for the command to complete. */ - R_TRY(this->WaitCommandComplete()); - - /* Process any response. */ - if (command->response_type != ResponseType_R0) { - this->last_response_type = command->response_type; - this->GetResponse(this->last_response, sizeof(this->last_response), this->last_response_type); - } - - /* Wait for data to be transferred. */ - if (xfer_data != nullptr) { - R_TRY(this->WaitTransferComplete()); - } - } - - /* If data was transferred, ensure we're in a consistent state. */ - if (xfer_data != nullptr) { - /* Ensure the cpu sees consistent data with the device. */ - if (xfer_data->transfer_direction == TransferDirection_ReadFromDevice) { - dd::InvalidateDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); - } - - /* Set the number of transferred blocks. */ - if (out_num_transferred_blocks != nullptr) { - *out_num_transferred_blocks = num_transferred_blocks; - } - - /* Process stop transition command. */ - this->last_stop_transmission_response = this->registers->response[3]; - } - - /* Wait until we're no longer busy. */ - if (command->is_busy || (xfer_data != nullptr)) { - R_TRY(this->WaitWhileBusy()); - } - - return ResultSuccess(); - } - - Result SdHostStandardController::IssueStopTransmissionCommandWithDeviceClock(u32 *out_response) { - /* Wait until we can issue the command. */ - R_TRY(this->WaitWhileCommandInhibit(false)); - - /* Issue the command with interrupt status enabled. */ - constexpr ResponseType StopTransmissionCommandResponseType = ResponseType_R1; - { - this->EnableInterruptStatus(); - ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; - - /* Set the command. */ - Command command(CommandIndex_StopTransmission, 0, StopTransmissionCommandResponseType, true); - this->SetCommand(std::addressof(command), false); - - /* Wait for the command to complete. */ - R_TRY(this->WaitCommandComplete()); - } - - /* Process response. */ - this->GetResponse(out_response, sizeof(u32), StopTransmissionCommandResponseType); - - /* Wait until we're done. */ - R_TRY(this->WaitWhileBusy()); - - return ResultSuccess(); - } - - SdHostStandardController::SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size) { - /* Translate the physical address to a address. */ - const uintptr_t registers_addr = dd::QueryIoMapping(registers_phys_addr, registers_size); - - /* Set registers. */ - AMS_ABORT_UNLESS(registers_addr != 0); - this->registers = reinterpret_cast(registers_addr); - - /* Reset DMA buffers, if we have any. */ - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - this->ResetBufferInfos(); - #endif - - /* Clear removed event, if we have one. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - this->removed_event = nullptr; - #endif - - /* Clear dma address. */ - this->next_sdma_address = 0; - this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; - - /* Clear clock/power trackers. */ - this->device_clock_frequency_khz = 0; - this->is_power_saving_enable = false; - this->is_device_clock_enable = false; - - /* Clear last response. */ - this->last_response_type = ResponseType_R0; - std::memset(this->last_response, 0, sizeof(this->last_response)); - this->last_stop_transmission_response = 0; - } - - void SdHostStandardController::Initialize() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - os::InitializeWaitableManager(std::addressof(this->waitable_manager)); - - AMS_ABORT_UNLESS(this->interrupt_event != nullptr); - os::InitializeWaitableHolder(std::addressof(this->interrupt_event_holder), this->interrupt_event); - os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->interrupt_event_holder)); - - if (this->removed_event != nullptr) { - os::InitializeWaitableHolder(std::addressof(this->removed_event_holder), this->removed_event); - os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->removed_event_holder)); - } - } - #endif - } - - void SdHostStandardController::Finalize() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - if (this->removed_event != nullptr) { - os::UnlinkWaitableHolder(std::addressof(this->removed_event_holder)); - os::FinalizeWaitableHolder(std::addressof(this->removed_event_holder)); - } - - os::UnlinkWaitableHolder(std::addressof(this->interrupt_event_holder)); - os::FinalizeWaitableHolder(std::addressof(this->interrupt_event_holder)); - - os::FinalizeWaitableManager(std::addressof(this->waitable_manager)); - } - #endif - } - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - void SdHostStandardController::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - /* Find and set a free info. */ - for (auto &info : this->buffer_infos) { - if (info.buffer_address == 0) { - info = { - .buffer_address = buffer, - .buffer_size = buffer_size, - .buffer_device_virtual_address = buffer_device_virtual_address, - }; - return; - } - } - - AMS_ABORT("Out of BufferInfos\n"); - } - - void SdHostStandardController::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - /* Find and clear the buffer info. */ - for (auto &info : this->buffer_infos) { - if (info.buffer_address == 0) { - AMS_ABORT_UNLESS(info.buffer_size == buffer_size); - AMS_ABORT_UNLESS(info.buffer_device_virtual_address == buffer_device_virtual_address); - - info.buffer_address = 0; - info.buffer_size = 0; - return; - } - } - - AMS_ABORT("BufferInfo not found\n"); - } - #endif - - void SdHostStandardController::SetWorkBuffer(void *wb, size_t wb_size) { - AMS_UNUSED(wb, wb_size); - AMS_ABORT("WorkBuffer is not needed\n"); - } - - BusPower SdHostStandardController::GetBusPower() const { - /* Check if the bus has power. */ - if (reg::HasValue(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON))) { - /* If it does, return the corresponding power. */ - switch (reg::GetValue(this->registers->power_control, SD_REG_BITS_MASK(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1))) { - case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_1_8V: - return BusPower_1_8V; - case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_3_3V: - return BusPower_3_3V; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } else { - /* It doesn't, so it's off. */ - return BusPower_Off; - } - } - - void SdHostStandardController::SetBusWidth(BusWidth bus_width) { - /* Check that we support the bus width. */ - AMS_ABORT_UNLESS(this->IsSupportedBusWidth(bus_width)); - - /* Set the appropriate data transfer width. */ - switch (bus_width) { - case BusWidth_1Bit: - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, ONE_BIT)); - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); - break; - case BusWidth_4Bit: - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT)); - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); - break; - case BusWidth_8Bit: - reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - BusWidth SdHostStandardController::GetBusWidth() const { - /* Check if the bus is using eight-bit extended data transfer. */ - if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT))) { - return BusWidth_8Bit; - } else { - /* Bus is configured as USE_DATA_TRANSFER_WIDTH, so check if it's four bit. */ - if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT))) { - return BusWidth_4Bit; - } else { - return BusWidth_1Bit; - } - } - } - - void SdHostStandardController::SetPowerSaving(bool en) { - /* Set whether we're power saving enable. */ - this->is_power_saving_enable = en; - - /* Configure accordingly. */ - if (this->is_power_saving_enable) { - /* We want to disable SD clock if it's enabled. */ - if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE))) { - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - } - } else { - /* We want to enable SD clock if it's disabled and we're supposed to enable device clock. */ - if (this->is_device_clock_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - } - } - } - - void SdHostStandardController::EnableDeviceClock() { - /* If we're not in power-saving mode and the device clock is disabled, enable it. */ - if (!this->is_power_saving_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - } - this->is_device_clock_enable = true; - } - - void SdHostStandardController::DisableDeviceClock() { - /* Unconditionally disable the device clock. */ - this->is_device_clock_enable = false; - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - } - - void SdHostStandardController::ChangeCheckTransferInterval(u32 ms) { - this->check_transfer_interval_ms = ms; - } - - void SdHostStandardController::SetDefaultCheckTransferInterval() { - this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; - } - - Result SdHostStandardController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { - /* We need to have device clock enabled to issue commands. */ - AMS_ABORT_UNLESS(this->is_device_clock_enable); - - /* Check if we need to temporarily re-enable the device clock. */ - const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - - /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ - if (clock_disabled) { - /* Turn on the clock. */ - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - - /* Ensure that our configuration takes. */ - this->EnsureControl(); - - /* Wait 8 device clocks to be sure that it's usable. */ - WaitClocks(8, this->device_clock_frequency_khz); - } - ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; - - /* Issue the command. */ - { - /* After we issue the command, we need to wait 8 device clocks. */ - ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; - - return this->IssueCommandWithDeviceClock(command, xfer_data, out_num_transferred_blocks); - } - } - - Result SdHostStandardController::IssueStopTransmissionCommand(u32 *out_response) { - /* We need to have device clock enabled to issue commands. */ - AMS_ABORT_UNLESS(this->is_device_clock_enable); - - /* Check if we need to temporarily re-enable the device clock. */ - const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - - /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ - if (clock_disabled) { - /* Turn on the clock. */ - reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - - /* Ensure that our configuration takes. */ - this->EnsureControl(); - - /* Wait 8 device clocks to be sure that it's usable. */ - WaitClocks(8, this->device_clock_frequency_khz); - } - ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; - - /* Issue the command. */ - { - /* After we issue the command, we need to wait 8 device clocks. */ - ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; - - return this->IssueStopTransmissionCommandWithDeviceClock(out_response); - } - } - - void SdHostStandardController::GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { - /* Check that we can get the response. */ - AMS_ABORT_UNLESS(out_response != nullptr); - AMS_ABORT_UNLESS(response_type == this->last_response_type); - - /* Get the response appropriately. */ - switch (response_type) { - case ResponseType_R1: - case ResponseType_R3: - case ResponseType_R6: - case ResponseType_R7: - /* 32-bit response. */ - AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); - out_response[0] = this->last_response[0]; - break; - case ResponseType_R2: - /* 128-bit response. */ - AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); - out_response[0] = this->last_response[0]; - out_response[1] = this->last_response[1]; - out_response[2] = this->last_response[2]; - out_response[3] = this->last_response[3]; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - void SdHostStandardController::GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const { - /* Check that we can get the response. */ - AMS_ABORT_UNLESS(out_response != nullptr); - AMS_ABORT_UNLESS(response_size >= sizeof(u32)); - - /* Get the response. */ - out_response[0] = this->last_stop_transmission_response; - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp deleted file mode 100644 index 7bdf0a3..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_i_host_controller.hpp" -#include "sdmmc_sd_host_standard_registers.hpp" - -namespace ams::sdmmc::impl { - - class SdHostStandardController : public IHostController { - protected: - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - struct BufferInfo { - uintptr_t buffer_address; - size_t buffer_size; - dd::DeviceVirtualAddress buffer_device_virtual_address; - }; - - static constexpr inline auto NumBufferInfos = 3; - #endif - protected: - SdHostStandardRegisters *registers; - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - BufferInfo buffer_infos[NumBufferInfos]; - #endif - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - os::WaitableManagerType waitable_manager; - os::InterruptEventType *interrupt_event; - os::WaitableHolderType interrupt_event_holder; - os::EventType *removed_event; - os::WaitableHolderType removed_event_holder; - #endif - - u64 next_sdma_address; - u32 check_transfer_interval_ms; - - u32 device_clock_frequency_khz; - bool is_power_saving_enable; - bool is_device_clock_enable; - - ResponseType last_response_type; - u32 last_response[4]; - u32 last_stop_transmission_response; - protected: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - void PreSetInterruptEvent(os::InterruptEventType *ie) { - this->interrupt_event = ie; - } - - bool IsRemoved() const { - return this->removed_event != nullptr && os::TryWaitEvent(this->removed_event); - } - #endif - - void SetDeviceClockFrequencyKHz(u32 khz) { - this->device_clock_frequency_khz = khz; - } - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - void ResetBufferInfos(); - dd::DeviceVirtualAddress GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size); - #endif - - void EnsureControl(); - Result EnableInternalClock(); - void SetBusPower(BusPower bus_power); - - void EnableInterruptStatus(); - void DisableInterruptStatus(); - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - Result WaitInterrupt(u32 timeout_ms); - void ClearInterrupt(); - #endif - - void SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data); - void SetTransferForTuning(); - - void SetCommand(const Command *command, bool has_xfer_data); - void SetCommandForTuning(u32 command_index); - - Result ResetCmdDatLine(); - Result AbortTransaction(); - Result WaitWhileCommandInhibit(bool has_dat); - Result CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask); - Result WaitCommandComplete(); - Result WaitTransferComplete(); - Result WaitWhileBusy(); - - void GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const; - - Result IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks); - Result IssueStopTransmissionCommandWithDeviceClock(u32 *out_response); - - ALWAYS_INLINE Result CheckRemoved() { - #if defined(AMS_SDMMC_USE_OS_EVENTS) - R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved()); - #endif - - return ResultSuccess(); - } - public: - SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size); - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual void PreSetRemovedEvent(os::EventType *e) override { - this->removed_event = e; - } - #endif - - virtual void Initialize() override; - virtual void Finalize() override; - - #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; - virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override; - #endif - - virtual void SetWorkBuffer(void *wb, size_t wb_size) override; - - virtual Result SwitchToSdr12() override { - AMS_ABORT("SwitchToSdr12 not supported\n"); - } - - virtual BusPower GetBusPower() const override; - - virtual void SetBusWidth(BusWidth bus_width) override; - virtual BusWidth GetBusWidth() const override; - - virtual u32 GetDeviceClockFrequencyKHz() const override { - return this->device_clock_frequency_khz; - } - - virtual void SetPowerSaving(bool en) override; - virtual bool IsPowerSavingEnable() const override { - return this->is_power_saving_enable; - } - - virtual void EnableDeviceClock() override; - virtual void DisableDeviceClock() override; - virtual bool IsDeviceClockEnable() const override { - return this->is_device_clock_enable; - } - - virtual u32 GetMaxTransferNumBlocks() const override { - return SdHostStandardRegisters::BlockCountMax; - } - - virtual void ChangeCheckTransferInterval(u32 ms) override; - virtual void SetDefaultCheckTransferInterval() override; - - virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; - virtual Result IssueStopTransmissionCommand(u32 *out_response) override; - - virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const override; - virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const override; - - virtual bool IsSupportedTuning() const override { - return false; - } - - virtual Result Tuning(SpeedMode speed_mode, u32 command_index) { - AMS_UNUSED(speed_mode, command_index); - AMS_ABORT("Tuning not supported\n"); - } - - virtual void SaveTuningStatusForHs400() { - AMS_ABORT("SaveTuningStatusForHs400 not supported\n"); - } - - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp deleted file mode 100644 index e104374..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::sdmmc::impl { - - struct SdHostStandardRegisters { - volatile uint32_t dma_address; - volatile uint16_t block_size; - volatile uint16_t block_count; - volatile uint32_t argument; - volatile uint16_t transfer_mode; - volatile uint16_t command; - volatile uint32_t response[0x4]; - volatile uint32_t buffer; - volatile uint32_t present_state; - volatile uint8_t host_control; - volatile uint8_t power_control; - volatile uint8_t block_gap_control; - volatile uint8_t wake_up_control; - volatile uint16_t clock_control; - volatile uint8_t timeout_control; - volatile uint8_t software_reset; - volatile uint16_t normal_int_status; - volatile uint16_t error_int_status; - volatile uint16_t normal_int_enable; - volatile uint16_t error_int_enable; - volatile uint16_t normal_signal_enable; - volatile uint16_t error_signal_enable; - volatile uint16_t acmd12_err; - volatile uint16_t host_control2; - volatile uint32_t capabilities; - volatile uint32_t capabilities_1; - volatile uint32_t max_current; - volatile uint32_t _0x4c; - volatile uint16_t set_acmd12_error; - volatile uint16_t set_int_error; - volatile uint8_t adma_error; - volatile uint8_t _0x56[0x3]; - volatile uint32_t adma_address; - volatile uint32_t upper_adma_address; - volatile uint16_t preset_for_init; - volatile uint16_t preset_for_default; - volatile uint16_t preset_for_high; - volatile uint16_t preset_for_sdr12; - volatile uint16_t preset_for_sdr25; - volatile uint16_t preset_for_sdr50; - volatile uint16_t preset_for_sdr104; - volatile uint16_t preset_for_ddr50; - volatile uint32_t _0x70[0x23]; - volatile uint16_t slot_int_status; - volatile uint16_t host_version; - - static constexpr inline u16 BlockCountMax = 0xFFFF; - }; - static_assert(sizeof(SdHostStandardRegisters) == 0x100); - - constexpr inline size_t SdmaBufferBoundary = 512_KB; - - #define SD_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SD_HOST_STANDARD, NAME) - #define SD_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SD_HOST_STANDARD, NAME, VALUE) - #define SD_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SD_HOST_STANDARD, NAME, ENUM) - #define SD_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SD_HOST_STANDARD, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) - - #define DEFINE_SD_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SD_HOST_STANDARD, NAME, __OFFSET__, __WIDTH__) - #define DEFINE_SD_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE) - #define DEFINE_SD_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) - #define DEFINE_SD_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) - #define DEFINE_SD_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 (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) - - DEFINE_SD_REG(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, 0, 12); - DEFINE_SD_REG_THREE_BIT_ENUM(BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 12, 4_KB, 8_KB, 16_KB, 32_KB, 64_KB, 128_KB, 256_KB, 512_KB); - constexpr inline size_t SdHostStandardBlockSizeTransferBlockSizeMax = 0xFFF; - - DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DMA_ENABLE, 0, DISABLE, ENABLE); - DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_BLOCK_COUNT_ENABLE, 1, DISABLE, ENABLE); - DEFINE_SD_REG_TWO_BIT_ENUM(TRANSFER_MODE_AUTO_CMD_ENABLE, 2, DISABLE, CMD12_ENABLE, CMD23_ENABLE, AUTO_SELECT); - DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, 4, WRITE, READ); - DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_MULTI_BLOCK_SELECT, 5, SINGLE_BLOCK, MULTI_BLOCK); - - DEFINE_SD_REG_TWO_BIT_ENUM(COMMAND_RESPONSE_TYPE, 0, NO_RESPONSE, RESPONSE_LENGTH_136, RESPONSE_LENGTH_48, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE); - DEFINE_SD_REG_BIT_ENUM(COMMAND_CRC_CHECK, 3, DISABLE, ENABLE); - DEFINE_SD_REG_BIT_ENUM(COMMAND_INDEX_CHECK, 4, DISABLE, ENABLE); - DEFINE_SD_REG_BIT_ENUM(COMMAND_DATA_PRESENT, 5, NO_DATA_PRESENT, DATA_PRESENT); - DEFINE_SD_REG(COMMAND_COMMAND_INDEX, 8, 6); - constexpr inline size_t SdHostStandardCommandIndexMax = 0x3F; - - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, 0, READY, NOT_READY); - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, 1, READY, NOT_READY); - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, 20, LOW, HIGH); - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT1_LINE_SIGNAL_LEVEL, 21, LOW, HIGH); - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT2_LINE_SIGNAL_LEVEL, 22, LOW, HIGH); - DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT3_LINE_SIGNAL_LEVEL, 23, LOW, HIGH); - DEFINE_SD_REG(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 20, 4); - - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, 1, ONE_BIT, FOUR_BIT); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, 2, NORMAL_SPEED, HIGH_SPEED); - DEFINE_SD_REG_TWO_BIT_ENUM(HOST_CONTROL_DMA_SELECT, 3, SDMA, RESERVED1, ADMA2, ADMA2_OR_ADMA3); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, 5, USE_DATA_TRANSFER_WIDTH, EIGHT_BIT); - - DEFINE_SD_REG_BIT_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, 0, OFF, ON); - DEFINE_SD_REG_THREE_BIT_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1, RESERVED0, RESERVED1, RESERVED2, RESERVED3, RESERVED4, 1_8V, 3_0V, 3_3V); - - DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, 0, STOP, OSCILLATE); - DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, 1, NOT_READY, READY); - DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, 2, DISABLE, ENABLE); - DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, 5, DIVIDED_CLOCK, PROGRAMMABLE_CLOCK); - DEFINE_SD_REG(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, 6, 2); - DEFINE_SD_REG(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, 8, 8); - - DEFINE_SD_REG(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0, 4); - - DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_ALL, 0, WORK, RESET); - DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_CMD, 1, WORK, RESET); - DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_DAT, 2, WORK, RESET); - - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, 0, NOT_COMPLETE, COMPLETE); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, 1, NOT_COMPLETE, COMPLETE); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, 3, NOT_GENERATED, GENERATED); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, 15, NO_ERROR, ERROR); - - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, 0, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, 1, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, 2, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, 3, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, 4, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, 5, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, 6, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, 8, NO_ERROR, ERROR); - - DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, 1, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, 2, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, 3, NO_ERROR, ERROR); - DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, 4, NO_ERROR, ERROR); - - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, 0, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, 1, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, 3, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, 5, MASKED, ENABLED); - - #define SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, __ENUM__), \ - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, __ENUM__), \ - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, __ENUM__) - - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, 1, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, 5, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, 6, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, 8, MASKED, ENABLED); - - #define SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, __ENUM__) - - - DEFINE_SD_REG_THREE_BIT_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, 0, SDR12, SDR25, SDR50, SDR104, DDR50, HS400, RSVD6, UHS_II); - - constexpr inline auto SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_HS200 = SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_SDR104; - - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3, 3_3V_SIGNALING, 1_8V_SIGNALING); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_EXECUTE_TUNING, 6, TUNING_COMPLETED, EXECUTE_TUNING); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, 7, USING_FIXED_CLOCK, USING_TUNED_CLOCK); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, 12, VERSION_300_COMPATIBLE, VERSION_4); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 13, 32_BIT_ADDRESSING, 64_BIT_ADDRESSING); - DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, 15, HOST_DRIVER, AUTOMATIC_SELECTION); - - DEFINE_SD_REG_BIT_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, 28, NOT_SUPPORTED, SUPPORTED); - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp deleted file mode 100644 index 3b16008..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp +++ /dev/null @@ -1,1311 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" -#include "sdmmc_io_impl.board.nintendo_nx.hpp" -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl { - - /* FOR REFERENCE: board-specific sdmmc registers. */ - //struct SdmmcRegisters { - // /* Standard registers. */ - // volatile SdHostStandardRegisters sd_host_standard_registers; - // - // /* Vendor specific registers */ - // volatile uint32_t vendor_clock_cntrl; - // volatile uint32_t vendor_sys_sw_cntrl; - // volatile uint32_t vendor_err_intr_status; - // volatile uint32_t vendor_cap_overrides; - // volatile uint32_t vendor_boot_cntrl; - // volatile uint32_t vendor_boot_ack_timeout; - // volatile uint32_t vendor_boot_dat_timeout; - // volatile uint32_t vendor_debounce_count; - // volatile uint32_t vendor_misc_cntrl; - // volatile uint32_t max_current_override; - // volatile uint32_t max_current_override_hi; - // volatile uint32_t _0x12c[0x20]; - // volatile uint32_t vendor_io_trim_cntrl; - // - // /* Start of sdmmc2/sdmmc4 only */ - // volatile uint32_t vendor_dllcal_cfg; - // volatile uint32_t vendor_dll_ctrl0; - // volatile uint32_t vendor_dll_ctrl1; - // volatile uint32_t vendor_dllcal_cfg_sta; - // /* End of sdmmc2/sdmmc4 only */ - // - // volatile uint32_t vendor_tuning_cntrl0; - // volatile uint32_t vendor_tuning_cntrl1; - // volatile uint32_t vendor_tuning_status0; - // volatile uint32_t vendor_tuning_status1; - // volatile uint32_t vendor_clk_gate_hysteresis_count; - // volatile uint32_t vendor_preset_val0; - // volatile uint32_t vendor_preset_val1; - // volatile uint32_t vendor_preset_val2; - // volatile uint32_t sdmemcomppadctrl; - // volatile uint32_t auto_cal_config; - // volatile uint32_t auto_cal_interval; - // volatile uint32_t auto_cal_status; - // volatile uint32_t io_spare; - // volatile uint32_t sdmmca_mccif_fifoctrl; - // volatile uint32_t timeout_wcoal_sdmmca; - // volatile uint32_t _0x1fc; - //}; - - DEFINE_SD_REG_BIT_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, 2, NORMAL, OVERRIDE); - DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TAP_VAL, 16, 8); - DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TRIM_VAL, 24, 5); - - DEFINE_SD_REG(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 8, 6); - - DEFINE_SD_REG(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 2, 1); - - DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, 31, DISABLE, ENABLE); - - DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, 31, DONE, RUNNING); - - DEFINE_SD_REG(VENDOR_TUNING_CNTRL0_MUL_M, 6, 7); - DEFINE_SD_REG_THREE_BIT_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, 13, TRIES_40, TRIES_64, TRIES_128, TRIES_192, TRIES_256, RESERVED5, RESERVED6, RESERVED7); - DEFINE_SD_REG_BIT_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, 17, NOT_UPDATED_BY_HW, UPDATED_BY_HW); - - DEFINE_SD_REG(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, 0, 4); - DEFINE_SD_REG(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 31, 1); - - DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, 0, 7); - DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, 8, 7); - DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, 29, DISABLED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLED, ENABLED); - - DEFINE_SD_REG(AUTO_CAL_STATUS_AUTO_CAL_PULLUP, 0, 7); - DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, 31, INACTIVE, ACTIVE); - - DEFINE_SD_REG_BIT_ENUM(IO_SPARE_SPARE_OUT_3, 19, TWO_CYCLE_DELAY, ONE_CYCLE_DELAY); - - namespace { - - constexpr inline u32 TuningCommandTimeoutMilliSeconds = 5; - - constexpr void GetDividerSetting(u32 *out_target_clock_frequency_khz, u16 *out_x, SpeedMode speed_mode) { - switch (speed_mode) { - case SpeedMode_MmcIdentification: - *out_target_clock_frequency_khz = 26000; - *out_x = 66; - break; - case SpeedMode_MmcLegacySpeed: - *out_target_clock_frequency_khz = 26000; - *out_x = 1; - break; - case SpeedMode_MmcHighSpeed: - *out_target_clock_frequency_khz = 52000; - *out_x = 1; - break; - case SpeedMode_MmcHs200: - *out_target_clock_frequency_khz = 200000; - *out_x = 1; - break; - case SpeedMode_MmcHs400: - *out_target_clock_frequency_khz = 200000; - *out_x = 1; - break; - case SpeedMode_SdCardIdentification: - *out_target_clock_frequency_khz = 25000; - *out_x = 64; - break; - case SpeedMode_SdCardDefaultSpeed: - *out_target_clock_frequency_khz = 25000; - *out_x = 1; - break; - case SpeedMode_SdCardHighSpeed: - *out_target_clock_frequency_khz = 50000; - *out_x = 1; - break; - case SpeedMode_SdCardSdr12: - *out_target_clock_frequency_khz = 25000; - *out_x = 1; - break; - case SpeedMode_SdCardSdr50: - *out_target_clock_frequency_khz = 100000; - *out_x = 1; - break; - case SpeedMode_SdCardSdr104: - *out_target_clock_frequency_khz = 200000; - *out_x = 1; - break; - case SpeedMode_GcAsicFpgaSpeed: - *out_target_clock_frequency_khz = 40800; - *out_x = 1; - break; - case SpeedMode_GcAsicSpeed: - *out_target_clock_frequency_khz = 200000; - *out_x = 2; - break; - case SpeedMode_SdCardSdr25: - case SpeedMode_SdCardDdr50: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - } - - namespace { - - #if defined(AMS_SDMMC_THREAD_SAFE) - constinit os::SdkMutex g_soc_mutex; - - #define AMS_SDMMC_LOCK_SOC_MUTEX() std::scoped_lock lk(g_soc_mutex) - - #else - - #define AMS_SDMMC_LOCK_SOC_MUTEX() - - #endif - - constinit bool g_determined_soc = false; - constinit bool g_is_soc_mariko = false; - - } - - bool IsSocMariko() { - if (!g_determined_soc) { - /* Ensure we have exclusive access to the soc variables. */ - AMS_SDMMC_LOCK_SOC_MUTEX(); - - /* Check the SocType. */ - #if defined(ATMOSPHERE_IS_EXOSPHERE) - { - g_is_soc_mariko = fuse::GetSocType() == fuse::SocType_Mariko; - } - #elif defined(ATMOSPHERE_IS_MESOSPHERE) - { - MESOSPHERE_TODO("Detect mariko via KSystemControl call?"); - } - #elif defined(ATMOSPHERE_IS_STRATOSPHERE) - { - /* Connect to spl for the duration of our check. */ - spl::Initialize(); - ON_SCOPE_EXIT { spl::Finalize(); }; - - g_is_soc_mariko = spl::GetSocType() == spl::SocType_Mariko; - } - #else - #error "Unknown execution context for ams::sdmmc::impl::IsSocMariko" - #endif - - /* Note that we determined the soc. */ - g_determined_soc = true; - } - - return g_is_soc_mariko; - } - - void SdmmcController::ReleaseReset(SpeedMode speed_mode) { - /* Get the clock reset module. */ - const auto module = this->GetClockResetModule(); - - /* If the module is available, disable clock. */ - if (ClockResetController::IsAvailable(module)) { - SdHostStandardController::DisableDeviceClock(); - SdHostStandardController::EnsureControl(); - } - - /* Get the correct divider setting for the speed mode. */ - u32 target_clock_frequency_khz; - u16 x; - GetDividerSetting(std::addressof(target_clock_frequency_khz), std::addressof(x), speed_mode); - - /* Release reset. */ - ClockResetController::ReleaseReset(module, target_clock_frequency_khz); - } - - void SdmmcController::AssertReset() { - return ClockResetController::AssertReset(this->GetClockResetModule()); - } - - Result SdmmcController::StartupCore(BusPower bus_power) { - /* Set schmitt trigger. */ - this->SetSchmittTrigger(bus_power); - - /* Select one-cycle delay version of cmd_oen. */ - reg::ReadWrite(this->sdmmc_registers->io_spare, SD_REG_BITS_ENUM(IO_SPARE_SPARE_OUT_3, ONE_CYCLE_DELAY)); - - /* Select regulated reference voltage for trimmer and DLL supply. */ - reg::ReadWrite(this->sdmmc_registers->vendor_io_trim_cntrl, SD_REG_BITS_VALUE(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 0)); - - /* Configure outbound tap value. */ - reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TRIM_VAL, this->GetOutboundTapValue())); - - /* Configure SPI_MODE_CLKEN_OVERRIDE. */ - reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, NORMAL)); - - /* Set slew codes. */ - this->SetSlewCodes(); - - /* Set vref sel. */ - reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, this->GetVrefSelValue())); - - /* Perform drive strength calibration at the new power. */ - this->SetDriveCodeOffsets(bus_power); - this->CalibrateDriveStrength(bus_power); - - /* Enable internal clock. */ - R_TRY(SdHostStandardController::EnableInternalClock()); - - return ResultSuccess(); - } - - Result SdmmcController::SetClockTrimmer(SpeedMode speed_mode, u8 tap_value) { - /* If speed mode is Hs400, set the dqs trim value. */ - if (speed_mode == SpeedMode_MmcHs400) { - reg::ReadWrite(this->sdmmc_registers->vendor_cap_overrides, SD_REG_BITS_VALUE(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 40)); - } - - /* Configure tap value as updated by software. */ - reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, NOT_UPDATED_BY_HW)); - - /* Set the inbound tap value. */ - reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TAP_VAL, tap_value)); - - /* Reset the cmd/dat line. */ - R_TRY(SdHostStandardController::ResetCmdDatLine()); - - return ResultSuccess(); - } - - u8 SdmmcController::GetCurrentTapValue() { - return static_cast(reg::GetValue(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_MASK(VENDOR_CLOCK_CNTRL_TAP_VAL))); - } - - Result SdmmcController::CalibrateDll() { - /* Check if we need to temporarily re-enable the device clock. */ - const bool clock_disabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - - /* Ensure that the clock is enabled for the period we're using it. */ - if (clock_disabled) { - /* Turn on the clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - } - ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; - - /* Begin calibration. */ - reg::ReadWrite(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE)); - - /* Wait up to 5ms for calibration to begin. */ - { - ManualTimer timer(5); - while (true) { - /* If calibration is done, we're done. */ - if (!reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE))) { - break; - } - - /* Otherwise, check if we've timed out. */ - R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllCalibrationSoftwareTimeout()); - } - } - - /* Wait up to 10ms for calibration to complete. */ - { - ManualTimer timer(10); - while (true) { - /* If calibration is done, we're done. */ - if (reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg_sta, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, DONE))) { - break; - } - - /* Otherwise, check if we've timed out. */ - R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllApplicationSoftwareTimeout()); - } - } - - return ResultSuccess(); - } - - Result SdmmcController::SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value) { - /* Check if we need to temporarily disable the device clock. */ - const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - - /* Ensure that the clock is disabled for the period we're using it. */ - if (clock_enabled) { - /* Turn off the clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - } - - /* Set clock trimmer. */ - /* NOTE: Nintendo does not re-enable the clock if this fails... */ - R_TRY(this->SetClockTrimmer(speed_mode, tap_value)); - - /* Configure for the desired speed mode. */ - switch (speed_mode) { - case SpeedMode_MmcIdentification: - case SpeedMode_SdCardIdentification: - case SpeedMode_MmcLegacySpeed: - case SpeedMode_SdCardDefaultSpeed: - /* Set as normal speed, 3.3V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, NORMAL_SPEED)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); - break; - case SpeedMode_MmcHighSpeed: - case SpeedMode_SdCardHighSpeed: - /* Set as high speed, 3.3V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, HIGH_SPEED)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); - break; - case SpeedMode_MmcHs200: - /* Set as HS200, 1.8V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS200)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); - break; - case SpeedMode_MmcHs400: - /* Set as HS400, 1.8V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS400)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); - break; - case SpeedMode_SdCardSdr12: - /* Set as SDR12, 1.8V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR12)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); - break; - case SpeedMode_SdCardSdr50: - case SpeedMode_SdCardSdr104: - case SpeedMode_GcAsicFpgaSpeed: - case SpeedMode_GcAsicSpeed: - /* Set as SDR104, 1.8V. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR104)); - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - SdHostStandardController::EnsureControl(); - - /* Get the divider setting. */ - u32 target_source_clock_frequency_khz; - u16 x; - GetDividerSetting(std::addressof(target_source_clock_frequency_khz), std::addressof(x), speed_mode); - - /* Set the clock frequency. */ - u32 actual_source_clock_frequency_khz; - ClockResetController::SetClockFrequencyKHz(std::addressof(actual_source_clock_frequency_khz), this->GetClockResetModule(), target_source_clock_frequency_khz); - - /* Set the device clock frequency. */ - const u32 actual_device_clock_frequency_khz = util::DivideUp(actual_source_clock_frequency_khz, x); - SdHostStandardController::SetDeviceClockFrequencyKHz(actual_device_clock_frequency_khz); - - /* Check that the divider is correct. */ - AMS_ABORT_UNLESS((x == 1) || util::IsAligned(x, 2)); - - /* Write the divider val to clock control. */ - const u16 n = x / 2; - const u16 upper_n = n >> 8; - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_VALUE(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, n), - SD_REG_BITS_VALUE(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, upper_n)); - - /* Re-enable the clock, if we should. */ - if (clock_enabled) { - /* Turn on the clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - } - - /* If speed mode is Hs400, calibrate dll. */ - if (speed_mode == SpeedMode_MmcHs400) { - R_TRY(this->CalibrateDll()); - } - - /* Set the current speed mode. */ - this->current_speed_mode = speed_mode; - - return ResultSuccess(); - } - - Result SdmmcController::IssueTuningCommand(u32 command_index) { - /* Check that we're not power saving enable. */ - AMS_ABORT_UNLESS(!SdHostStandardController::IsPowerSavingEnable()); - - /* Wait until command inhibit is done. */ - R_TRY(SdHostStandardController::WaitWhileCommandInhibit(true)); - - /* Set transfer for tuning. */ - SdHostStandardController::SetTransferForTuning(); - - /* If necessary, clear interrupt and enable buffer read ready signal. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - this->ClearInterrupt(); - - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); - } - #endif - - /* Set the buffer read ready enable, and read status to ensure it takes. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); - reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, reg::Read(this->sdmmc_registers->sd_host_standard_registers.normal_int_status)); - - /* Issue command with clock disabled. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - { - SdHostStandardController::SetCommandForTuning(command_index); - - SdHostStandardController::EnsureControl(); - WaitMicroSeconds(1); - SdHostStandardController::AbortTransaction(); - } - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - - /* When we're done waiting, ensure that we clean up appropriately. */ - ON_SCOPE_EXIT { - /* Clear the buffer read ready signal, if we should. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); - #endif - - /* Clear the buffer read ready enable. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); - - /* Wait 8 clocks to ensure configuration takes. */ - SdHostStandardController::EnsureControl(); - WaitClocks(8, SdHostStandardController::GetDeviceClockFrequencyKHz()); - }; - - /* Wait for the command to finish. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - const auto result = SdHostStandardController::WaitInterrupt(TuningCommandTimeoutMilliSeconds); - if (R_SUCCEEDED(result)) { - /* If we succeeded, clear the interrupt. */ - reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); - this->ClearInterrupt(); - return ResultSuccess(); - } else if (sdmmc::ResultWaitInterruptSoftwareTimeout::Includes(result)) { - SdHostStandardController::AbortTransaction(); - return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); - } else { - return result; - } - } - #else - { - SdHostStandardController::EnsureControl(); - ManualTimer timer(TuningCommandTimeoutMilliSeconds); - while (true) { - /* Check if we received the interrupt. */ - if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED))) { - /* If we did, acknowledge it. */ - reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); - return ResultSuccess(); - } - - /* Otherwise, check if we timed out. */ - if (!timer.Update()) { - SdHostStandardController::AbortTransaction(); - return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); - } - } - } - #endif - } - - void SdmmcController::SetDriveCodeOffsets(BusPower bus_power) { - /* Get the offsets. */ - u8 pd, pu; - this->GetAutoCalOffsets(std::addressof(pd), std::addressof(pu), bus_power); - - /* Set the offsets. */ - reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, pd), - SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, pu)); - - /* Wait for 1ms to ensure that our configuration takes. */ - /* NOTE/HACK: Nintendo does not (need) to do this, but they use SvcSleepThread for waits. */ - /* It's unclear why this wait is necessary, but not doing it causes drive strength calibration to fail if done immediately afterwards. */ - util::WaitMicroSeconds(1000); - } - - void SdmmcController::CalibrateDriveStrength(BusPower bus_power) { - /* Reset drive strength calibration status. */ - this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); - - /* Check if we need to temporarily disable the device clock. */ - const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - - /* Ensure that the clock is disabled for the period we're using it. */ - if (clock_enabled) { - /* Turn off the clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - } - - /* Calibrate with the clock disabled. */ - { - /* Set SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ - if (reg::HasValue(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0))) { - reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 1)); - SdHostStandardController::EnsureControl(); - WaitMicroSeconds(1); - } - - /* Calibrate with SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD set. */ - { - /* Begin autocal. */ - reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, ENABLED), - SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, ENABLED)); - SdHostStandardController::EnsureControl(); - WaitMicroSeconds(2); - - /* Wait up to 10ms for auto cal to complete. */ - ManualTimer timer(10); - while (true) { - /* Check if auto cal is inactive. */ - if (reg::HasValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, INACTIVE))) { - /* Check the pullup status. */ - const u32 pullup = (reg::GetValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_MASK(AUTO_CAL_STATUS_AUTO_CAL_PULLUP))) & 0x1F; - if (pullup == 0x1F) { - this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompShortToGnd(); - } - if (pullup == 0) { - this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompOpen(); - } - - break; - } - - /* Otherwise, check if we've timed out. */ - if (!timer.Update()) { - this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationSoftwareTimeout(); - - this->SetDriveStrengthToDefaultValues(bus_power); - reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, DISABLED)); - break; - } - } - } - - /* Clear SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ - reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0)); - } - - /* Re-enable the clock, if we should. */ - if (clock_enabled) { - /* Turn on the clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - } - - /* If calibration didn't receive a replacement error, set internal state to success. */ - if (sdmmc::ResultDriveStrengthCalibrationNotCompleted::Includes(this->drive_strength_calibration_status)) { - this->drive_strength_calibration_status = ResultSuccess(); - } - } - - Result SdmmcController::Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) { - /* Verify that we're awake. */ - AMS_ABORT_UNLESS(this->is_awake); - - /* Release the controller from reset. */ - this->ReleaseReset(speed_mode); - - /* Mark that we're not shutdown. */ - this->is_shutdown = false; - - /* Power on the controller. */ - R_TRY(this->PowerOn(bus_power)); - - /* Start up for the specific power. */ - R_TRY(this->StartupCore(bus_power)); - - /* Set our current power/width/speed. */ - SdHostStandardController::SetBusWidth(bus_width); - SdHostStandardController::SetBusPower(bus_power); - R_TRY(this->SetSpeedMode(speed_mode)); - this->SetPowerSaving(power_saving_enable); - - /* Enable clock to the device. */ - SdHostStandardController::EnableDeviceClock(); - - /* Ensure that we can control the device. */ - SdHostStandardController::EnsureControl(); - - return ResultSuccess(); - } - - void SdmmcController::Shutdown() { - /* If we're already shut down, there's nothing to do. */ - if (this->is_shutdown) { - return; - } - - /* If we're currently awake, we need to disable clock/power. */ - if (this->is_awake) { - SdHostStandardController::DisableDeviceClock(); - SdHostStandardController::SetBusPower(BusPower_Off); - SdHostStandardController::EnsureControl(); - } - - /* Power off. */ - this->PowerOff(); - - /* If awake, assert reset. */ - if (this->is_awake) { - this->AssertReset(); - } - - /* Mark that we're shutdown. */ - this->is_shutdown = true; - } - - void SdmmcController::PutToSleep() { - /* If we're already shut down or asleep, there's nothing to do. */ - if (this->is_shutdown || !this->is_awake) { - return; - } - - /* Save values before sleep. */ - this->bus_power_before_sleep = SdHostStandardController::GetBusPower(); - this->bus_width_before_sleep = SdHostStandardController::GetBusWidth(); - this->speed_mode_before_sleep = this->current_speed_mode; - this->tap_value_before_sleep = this->GetCurrentTapValue(); - this->is_powersaving_enable_before_sleep = SdHostStandardController::IsPowerSavingEnable(); - - /* Disable clock/power to the device. */ - SdHostStandardController::DisableDeviceClock(); - SdHostStandardController::SetBusPower(BusPower_Off); - SdHostStandardController::EnsureControl(); - - /* Assert reset. */ - this->AssertReset(); - - /* Mark that we're asleep. */ - this->is_awake = false; - } - - Result SdmmcController::Awaken() { - /* If we're shut down, or if we're awake already, there's nothing to do. */ - R_SUCCEED_IF(this->is_shutdown); - R_SUCCEED_IF(this->is_awake); - - /* Mark that we're awake. */ - this->is_awake = true; - - /* Clear pad parked status. */ - this->ClearPadParked(); - - /* Release reset. */ - this->ReleaseReset(this->speed_mode_before_sleep); - - /* Start up for the correct power. */ - R_TRY(this->StartupCore(this->bus_power_before_sleep)); - - /* Configure values to what they were before sleep. */ - SdHostStandardController::SetBusWidth(this->bus_width_before_sleep); - SdHostStandardController::SetBusPower(this->bus_power_before_sleep); - R_TRY(this->SetSpeedModeWithTapValue(this->speed_mode_before_sleep, this->tap_value_before_sleep)); - this->SetPowerSaving(this->is_powersaving_enable_before_sleep); - - /* Enable clock to the device. */ - SdHostStandardController::EnableDeviceClock(); - SdHostStandardController::EnsureControl(); - - return ResultSuccess(); - } - - Result SdmmcController::SwitchToSdr12() { - /* Disable clock. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); - - /* Check that the dat lines are all low. */ - R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b0000)), sdmmc::ResultSdCardNotReadyToVoltageSwitch()); - - /* Set Speed Mode. */ - R_TRY(this->SetSpeedMode(SpeedMode_SdCardSdr12)); - - /* Set voltage to 1.8V. */ - SdHostStandardController::EnsureControl(); - R_TRY(this->LowerBusPower()); - this->SetSchmittTrigger(BusPower_1_8V); - - /* Perform drive strength calibration at the new power. */ - this->SetDriveCodeOffsets(BusPower_1_8V); - this->CalibrateDriveStrength(BusPower_1_8V); - - /* Set the bus power in standard controller. */ - SdHostStandardController::SetBusPower(BusPower_1_8V); - - /* Wait up to 5ms for the switch to take. */ - SdHostStandardController::EnsureControl(); - WaitMicroSeconds(5000); - - /* Check that we switched to 1.8V. */ - R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)), sdmmc::ResultSdHostStandardFailSwitchTo1_8V()); - - /* Enable clock, and wait 1ms. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); - SdHostStandardController::EnsureControl(); - WaitMicroSeconds(1000); - - /* Check that the dat lines are all high. */ - R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b1111)), sdmmc::ResultSdCardNotCompleteVoltageSwitch()); - - return ResultSuccess(); - } - - Result SdmmcController::SetSpeedMode(SpeedMode speed_mode) { - /* Get the tap value. */ - u8 tap_value; - if (speed_mode == SpeedMode_MmcHs400) { - AMS_ABORT_UNLESS(this->is_valid_tap_value_for_hs_400); - tap_value = this->tap_value_for_hs_400; - } else { - tap_value = this->GetDefaultInboundTapValue(); - } - - /* Set the speed mode. */ - R_TRY(this->SetSpeedModeWithTapValue(speed_mode, tap_value)); - - return ResultSuccess(); - } - - void SdmmcController::SetPowerSaving(bool en) { - /* If necessary, calibrate the drive strength. */ - if (this->IsNeedPeriodicDriveStrengthCalibration() && !en && SdHostStandardController::IsDeviceClockEnable()) { - this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); - } - - return SdHostStandardController::SetPowerSaving(en); - } - - void SdmmcController::EnableDeviceClock() { - /* If necessary, calibrate the drive strength. */ - if (this->IsNeedPeriodicDriveStrengthCalibration() && !SdHostStandardController::IsPowerSavingEnable()) { - this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); - } - - return SdHostStandardController::EnableDeviceClock(); - } - - Result SdmmcController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { - /* If necessary, calibrate the drive strength. */ - if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { - this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); - } - - return SdHostStandardController::IssueCommand(command, xfer_data, out_num_transferred_blocks); - } - - Result SdmmcController::IssueStopTransmissionCommand(u32 *out_response) { - /* If necessary, calibrate the drive strength. */ - if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { - this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); - } - - return SdHostStandardController::IssueStopTransmissionCommand(out_response); - } - - Result SdmmcController::Tuning(SpeedMode speed_mode, u32 command_index) { - /* Clear vendor tuning control 1. */ - reg::Write(this->sdmmc_registers->vendor_tuning_cntrl1, 0); - - /* Determine/configure the number of tries. */ - int num_tries; - switch (speed_mode) { - case SpeedMode_MmcHs200: - case SpeedMode_MmcHs400: - case SpeedMode_SdCardSdr104: - num_tries = 128; - reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_128)); - break; - case SpeedMode_SdCardSdr50: - case SpeedMode_GcAsicFpgaSpeed: - case SpeedMode_GcAsicSpeed: - num_tries = 256; - reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_256)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Configure the multiplier. */ - reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_VALUE(VENDOR_TUNING_CNTRL0_MUL_M, 1)); - - /* Configure tap value to be updated by hardware. */ - reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, UPDATED_BY_HW)); - - /* Configure to execute tuning. */ - reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, EXECUTE_TUNING)); - - /* Perform tuning num_tries times. */ - for (int i = 0; /* ... */; ++i) { - /* Check if we've been removed. */ - R_TRY(this->CheckRemoved()); - - /* Issue the command. */ - this->IssueTuningCommand(command_index); - - /* Check if tuning is done. */ - if (i >= num_tries) { - break; - } - - if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, TUNING_COMPLETED))) { - break; - } - } - - /* Check if we're using the tuned clock. */ - R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, USING_TUNED_CLOCK)), sdmmc::ResultTuningFailed()); - - return ResultSuccess(); - } - - void SdmmcController::SaveTuningStatusForHs400() { - /* Save the current tap value. */ - this->tap_value_for_hs_400 = GetCurrentTapValue(); - this->is_valid_tap_value_for_hs_400 = true; - } - - Result Sdmmc1Controller::PowerOnForRegisterControl(BusPower bus_power) { - AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); - - /* Nintendo sets the current bus power regardless of whether the call succeeds. */ - ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; - - /* pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ - R_TRY(this->power_controller->PowerOn(BusPower_3_3V)); - - return ResultSuccess(); - } - - void Sdmmc1Controller::PowerOffForRegisterControl() { - /* If we're already off, there's nothing to do. */ - if (this->current_bus_power == BusPower_Off) { - return; - } - - /* If we're at 3.3V, lower to 1.8V. */ - if (this->current_bus_power == BusPower_3_3V) { - /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ - this->power_controller->LowerBusPower(); - - /* Set our bus power. */ - this->current_bus_power = BusPower_1_8V; - } - - /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ - pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1OutputHigh); - - - /* pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ - this->power_controller->PowerOff(); - - /* Set our bus power. */ - this->current_bus_power = BusPower_Off; - - /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ - pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1ResetState); - } - - Result Sdmmc1Controller::LowerBusPowerForRegisterControl() { - /* Nintendo sets the current bus power regardless of whether the call succeeds. */ - ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; - - /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ - R_TRY(this->power_controller->LowerBusPower()); - - return ResultSuccess(); - } - - void Sdmmc1Controller::SetSchmittTriggerForRegisterControl(BusPower bus_power) { - SdHostStandardController::EnsureControl(); - - if (IsSocMariko()) { - /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ - pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); - } else { - switch (bus_power) { - case BusPower_1_8V: - /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ - pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); - break; - case BusPower_3_3V: - /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ - pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtDisable); - break; - case BusPower_Off: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - } - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - Result Sdmmc1Controller::PowerOnForPcvControl(BusPower bus_power) { - AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); - - /* Nintendo sets the current bus power regardless of whether the call succeeds. */ - ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; - - /* TODO: return pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ - return ResultSuccess(); - } - - void Sdmmc1Controller::PowerOffForPcvControl() { - /* If we're already off, there's nothing to do. */ - if (this->current_bus_power == BusPower_Off) { - return; - } - - /* If we're at 3.3V, lower to 1.8V. */ - { - /* TODO: pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ - this->current_bus_power = BusPower_1_8V; - } - - /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ - - /* TODO: pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ - this->current_bus_power = BusPower_Off; - - /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ - - } - - Result Sdmmc1Controller::LowerBusPowerForPcvControl() { - /* Nintendo sets the current bus power regardless of whether the call succeeds. */ - ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; - - /* TODO: return pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ - return ResultSuccess(); - } - - void Sdmmc1Controller::SetSchmittTriggerForPcvControl(BusPower bus_power) { - SdHostStandardController::EnsureControl(); - - if (IsSocMariko()) { - /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ - } else { - switch (bus_power) { - case BusPower_1_8V: - /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ - break; - case BusPower_3_3V: - /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ - break; - case BusPower_Off: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - } - #endif - - Result Sdmmc1Controller::PowerOn(BusPower bus_power) { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - if (this->is_pcv_control) { - return this->PowerOnForPcvControl(bus_power); - } else - #endif - { - return this->PowerOnForRegisterControl(bus_power); - } - } - - void Sdmmc1Controller::PowerOff() { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - if (this->is_pcv_control) { - return this->PowerOffForPcvControl(); - } else - #endif - { - return this->PowerOffForRegisterControl(); - } - } - - Result Sdmmc1Controller::LowerBusPower() { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - if (this->is_pcv_control) { - return this->LowerBusPowerForPcvControl(); - } else - #endif - { - return this->LowerBusPowerForRegisterControl(); - } - } - - void Sdmmc1Controller::SetSchmittTrigger(BusPower bus_power) { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - if (this->is_pcv_control) { - return this->SetSchmittTriggerForPcvControl(bus_power); - } else - #endif - { - return this->SetSchmittTriggerForRegisterControl(bus_power); - } - } - - void Sdmmc1Controller::Initialize() { - return this->InitializeForRegisterControl(); - } - - void Sdmmc1Controller::Finalize() { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - if (this->is_pcv_control) { - return this->FinalizeForPcvControl(); - } else - #endif - { - return this->FinalizeForRegisterControl(); - } - } - - void Sdmmc1Controller::InitializeForRegisterControl() { - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - /* Mark ourselves as initialized by register control. */ - this->is_pcv_control = false; - #endif - - /* pinmux::Initialize(); */ - /* This just opens a session handle to pinmux service, no work to do. */ - - /* pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ - /* This just sets the session's internal value to the pin group name, so nothing to do here either. */ - - /* pcv::Initialize(); */ - /* This initializes a lot of globals in pcv, most of which we don't care about. */ - /* However, we do care about the Sdmmc1PowerController. */ - AMS_ABORT_UNLESS(this->power_controller == nullptr); - this->power_controller = new (GetPointer(this->power_controller_storage)) PowerController; - - /* Perform base initialization. */ - SdmmcController::Initialize(); - } - - void Sdmmc1Controller::FinalizeForRegisterControl() { - /* Perform base finalization. */ - SdmmcController::Finalize(); - - /* pcv::Finalize(); */ - /* As with initialize, we mostly don't care about the globals this touches. */ - /* However, we do want to finalize the Sdmmc1PowerController. */ - AMS_ABORT_UNLESS(this->power_controller != nullptr); - this->power_controller->~PowerController(); - this->power_controller = nullptr; - - /* pinmux::CloseSession(std::addressof(this->pinmux_session)); */ - /* This does nothing. */ - - /* pinmux::Finalize(); */ - /* This does nothing. */ - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - /* Mark ourselves as initialized by register control. */ - this->is_pcv_control = false; - #endif - } - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - void Sdmmc1Controller::InitializeForPcvControl() { - /* Mark ourselves as initialized by pcv control. */ - this->is_pcv_control = true; - - /* TODO: pinmux::Initialize(); */ - /* TODO: pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ - /* TODO: pcv::Initialize(); */ - - /* Perform base initialization. */ - SdmmcController::Initialize(); - } - - void Sdmmc1Controller::FinalizeForPcvControl() { - /* Perform base finalization. */ - SdmmcController::Finalize(); - - /* TODO: pcv::Finalize(); */ - /* TODO: pinmux::CloseSession(std::addressof(this->pinmux_session)); */ - /* TODO: pinmux::Finalize(); */ - - /* Mark ourselves as initialized by register control. */ - this->is_pcv_control = false; - } - #endif - - namespace { - - constexpr inline dd::PhysicalAddress PmcRegistersPhysicalAddress = 0x7000E400; - - } - - Result Sdmmc1Controller::PowerController::ControlVddioSdmmc1(BusPower bus_power) { - /* Configure appropriate voltage. */ - switch (bus_power) { - case BusPower_Off: - R_TRY(SetSdCardVoltageEnabled(false)); - break; - case BusPower_1_8V: - R_TRY(SetSdCardVoltageValue(1'800'000)); - R_TRY(SetSdCardVoltageEnabled(true)); - break; - case BusPower_3_3V: - R_TRY(SetSdCardVoltageValue(3'300'000)); - R_TRY(SetSdCardVoltageEnabled(true)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - return ResultSuccess(); - } - - void Sdmmc1Controller::PowerController::SetSdmmcIoMode(bool is_3_3V) { - /* Determine the address we're updating. */ - constexpr dd::PhysicalAddress ApbdevPmcPwrDetValAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_PWR_DET_VAL; - - /* Read the current value. */ - u32 value = dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); - - /* Mask out the existing bits. */ - value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(PWR_DET_VAL_SDMMC1))); - - /* ORR in the new bits. */ - value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(PWR_DET_VAL_SDMMC1, is_3_3V, ENABLE, DISABLE)); - - /* Write the new value. */ - dd::WriteIoRegister(ApbdevPmcPwrDetValAddress, value); - - /* Read the value back to be sure our write takes. */ - dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); - } - - void Sdmmc1Controller::PowerController::ControlRailSdmmc1Io(bool is_power_on) { - /* Determine the address we're updating. */ - constexpr dd::PhysicalAddress ApbdevPmcNoIoPowerAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_NO_IOPOWER; - - /* Read the current value. */ - u32 value = dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); - - /* Mask out the existing bits. */ - value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(NO_IOPOWER_SDMMC1))); - - /* ORR in the new bits. */ - value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(NO_IOPOWER_SDMMC1, is_power_on, DISABLE, ENABLE)); - - /* Write the new value. */ - dd::WriteIoRegister(ApbdevPmcNoIoPowerAddress, value); - - /* Read the value back to be sure our write takes. */ - dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); - } - - Sdmmc1Controller::PowerController::PowerController() : current_bus_power(BusPower_Off) { - /* gpio::Initialize(); */ - /* ... */ - - /* Open gpio session. */ - /* gpio::OpenSession(std::addressof(this->gpio_pad_session), gpio::GpioPadName_PowSdEn); */ - gpio_impl::OpenSession(gpio_impl::GpioPadName_PowSdEn); - - /* Configure the gpio as low/output. */ - /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ - gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); - - /* gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Output); */ - gpio_impl::SetDirection(gpio_impl::GpioPadName_PowSdEn, gpio_impl::Direction_Output); - } - - Sdmmc1Controller::PowerController::~PowerController() { - /* gpio::CloseSession(std::addressof(this->gpio_pad_session)); */ - gpio_impl::CloseSession(gpio_impl::GpioPadName_PowSdEn); - - /* gpio::Finalize(); */ - /* ... */ - } - - Result Sdmmc1Controller::PowerController::PowerOn(BusPower bus_power) { - /* Bus power should be off, and if it's not we don't need to do anything. */ - AMS_ASSERT(this->current_bus_power == BusPower_Off); - R_SUCCEED_IF(this->current_bus_power != BusPower_Off); - - /* Power on requires the target bus power be 3.3V. */ - AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); - - /* Enable the rail. */ - this->ControlRailSdmmc1Io(true); - - /* Set the SD power GPIO to high. */ - /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_High); */ - gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_High); - - /* Wait 10ms for power change to take. */ - WaitMicroSeconds(10000); - - /* Configure Sdmmc1 IO as 3.3V. */ - this->SetSdmmcIoMode(true); - R_TRY(this->ControlVddioSdmmc1(BusPower_3_3V)); - - /* Wait 130 us for changes to take. */ - WaitMicroSeconds(130); - - /* Update our current bus power. */ - this->current_bus_power = bus_power; - - return ResultSuccess(); - } - - Result Sdmmc1Controller::PowerController::PowerOff() { - /* Bus power should be on, and if it's not we don't need to do anything. */ - AMS_ASSERT(this->current_bus_power != BusPower_Off); - R_SUCCEED_IF(this->current_bus_power == BusPower_Off); - - /* Bus power should be 1.8V. */ - /* NOTE: the result returned here is 0x8C0 (regulator::ResultIllegalRequest()) on newer firmwares. */ - AMS_ASSERT(this->current_bus_power == BusPower_1_8V); - R_UNLESS(this->current_bus_power == BusPower_1_8V, pcv::ResultIllegalRequest()); - - /* Disable vddio, and wait 4 ms. */ - this->ControlVddioSdmmc1(BusPower_Off); - WaitMicroSeconds(4000); - - /* Set the SD power GPIO to low. */ - /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ - gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); - - /* Wait 239ms for the gpio config to take. */ - WaitMicroSeconds(239000); - - /* Disable the rail. */ - this->ControlRailSdmmc1Io(false); - this->SetSdmmcIoMode(true); - - /* Update our current bus power. */ - this->current_bus_power = BusPower_Off; - - return ResultSuccess(); - } - - Result Sdmmc1Controller::PowerController::LowerBusPower() { - /* Bus power should be 3.3V, and if it's not we don't need to do anything. */ - AMS_ASSERT(this->current_bus_power == BusPower_3_3V); - R_SUCCEED_IF(this->current_bus_power != BusPower_3_3V); - - /* Configure as 1.8V, then wait 150us for it to take. */ - R_TRY(this->ControlVddioSdmmc1(BusPower_1_8V)); - WaitMicroSeconds(150); - this->SetSdmmcIoMode(false); - - /* Update our current bus power. */ - this->current_bus_power = BusPower_1_8V; - - return ResultSuccess(); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp deleted file mode 100644 index d007d9d..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp +++ /dev/null @@ -1,682 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include -#include "sdmmc_sd_host_standard_controller.hpp" -#include "sdmmc_clock_reset_controller.hpp" - -namespace ams::sdmmc::impl { - - bool IsSocMariko(); - - constexpr inline size_t SdmmcRegistersSize = 0x200; - - constexpr inline dd::PhysicalAddress ApbMiscRegistersPhysicalAddress = UINT64_C(0x70000000); - constexpr inline size_t ApbMiscRegistersSize = 16_KB; - - class SdmmcController : public SdHostStandardController { - private: - struct SdmmcRegisters { - /* Standard registers. */ - volatile SdHostStandardRegisters sd_host_standard_registers; - - /* Vendor specific registers */ - volatile uint32_t vendor_clock_cntrl; - volatile uint32_t vendor_sys_sw_cntrl; - volatile uint32_t vendor_err_intr_status; - volatile uint32_t vendor_cap_overrides; - volatile uint32_t vendor_boot_cntrl; - volatile uint32_t vendor_boot_ack_timeout; - volatile uint32_t vendor_boot_dat_timeout; - volatile uint32_t vendor_debounce_count; - volatile uint32_t vendor_misc_cntrl; - volatile uint32_t max_current_override; - volatile uint32_t max_current_override_hi; - volatile uint32_t _0x12c[0x20]; - volatile uint32_t vendor_io_trim_cntrl; - - /* Start of sdmmc2/sdmmc4 only */ - volatile uint32_t vendor_dllcal_cfg; - volatile uint32_t vendor_dll_ctrl0; - volatile uint32_t vendor_dll_ctrl1; - volatile uint32_t vendor_dllcal_cfg_sta; - /* End of sdmmc2/sdmmc4 only */ - - volatile uint32_t vendor_tuning_cntrl0; - volatile uint32_t vendor_tuning_cntrl1; - volatile uint32_t vendor_tuning_status0; - volatile uint32_t vendor_tuning_status1; - volatile uint32_t vendor_clk_gate_hysteresis_count; - volatile uint32_t vendor_preset_val0; - volatile uint32_t vendor_preset_val1; - volatile uint32_t vendor_preset_val2; - volatile uint32_t sdmemcomppadctrl; - volatile uint32_t auto_cal_config; - volatile uint32_t auto_cal_interval; - volatile uint32_t auto_cal_status; - volatile uint32_t io_spare; - volatile uint32_t sdmmca_mccif_fifoctrl; - volatile uint32_t timeout_wcoal_sdmmca; - volatile uint32_t _0x1fc; - }; - static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize); - private: - SdmmcRegisters *sdmmc_registers; - bool is_shutdown; - bool is_awake; - SpeedMode current_speed_mode; - BusPower bus_power_before_sleep; - BusWidth bus_width_before_sleep; - SpeedMode speed_mode_before_sleep; - u8 tap_value_before_sleep; - bool is_powersaving_enable_before_sleep; - u8 tap_value_for_hs_400; - bool is_valid_tap_value_for_hs_400; - Result drive_strength_calibration_status; - private: - void ReleaseReset(SpeedMode speed_mode); - void AssertReset(); - Result StartupCore(BusPower bus_power); - Result SetClockTrimmer(SpeedMode speed_mode, u8 tap_value); - u8 GetCurrentTapValue(); - Result CalibrateDll(); - Result SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value); - Result IssueTuningCommand(u32 command_index); - protected: - void SetDriveCodeOffsets(BusPower bus_power); - void CalibrateDriveStrength(BusPower bus_power); - - virtual void SetPad() = 0; - - virtual ClockResetController::Module GetClockResetModule() const = 0; - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual int GetInterruptNumber() const = 0; - virtual os::InterruptEventType *GetInterruptEvent() const = 0; - #endif - - virtual bool IsNeedPeriodicDriveStrengthCalibration() = 0; - virtual void ClearPadParked() = 0; - virtual Result PowerOn(BusPower bus_power) = 0; - virtual void PowerOff() = 0; - virtual Result LowerBusPower() = 0; - virtual void SetSchmittTrigger(BusPower bus_power) = 0; - virtual u8 GetOutboundTapValue() const = 0; - virtual u8 GetDefaultInboundTapValue() const = 0; - virtual u8 GetVrefSelValue() const = 0; - virtual void SetSlewCodes() = 0; - virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const = 0; - virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) = 0; - public: - explicit SdmmcController(dd::PhysicalAddress registers_phys_addr) : SdHostStandardController(registers_phys_addr, SdmmcRegistersSize) { - /* Set sdmmc registers. */ - static_assert(offsetof(SdmmcRegisters, sd_host_standard_registers) == 0); - this->sdmmc_registers = reinterpret_cast(this->registers); - - this->is_shutdown = true; - this->is_awake = true; - this->is_valid_tap_value_for_hs_400 = false; - this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); - this->tap_value_for_hs_400 = 0; - this->current_speed_mode = SpeedMode_MmcIdentification; - this->bus_power_before_sleep = BusPower_Off; - this->bus_width_before_sleep = BusWidth_1Bit; - this->speed_mode_before_sleep = SpeedMode_MmcIdentification; - this->tap_value_before_sleep = 0; - this->is_powersaving_enable_before_sleep = false; - } - - virtual void Initialize() override { - /* Set pad. */ - this->SetPad(); - - /* Initialize our clock/reset module. */ - ClockResetController::Initialize(this->GetClockResetModule()); - - /* If necessary, initialize our interrupt event. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - os::InterruptEventType *interrupt_event = this->GetInterruptEvent(); - os::InitializeInterruptEvent(interrupt_event, this->GetInterruptNumber(), os::EventClearMode_ManualClear); - SdHostStandardController::PreSetInterruptEvent(interrupt_event); - } - #endif - - /* Perform base initialization. */ - SdHostStandardController::Initialize(); - } - - virtual void Finalize() override { - /* Perform base finalization. */ - SdHostStandardController::Finalize(); - - /* If necessary, finalize our interrupt event. */ - #if defined(AMS_SDMMC_USE_OS_EVENTS) - { - os::FinalizeInterruptEvent(this->GetInterruptEvent()); - } - #endif - - /* Finalize our clock/reset module. */ - ClockResetController::Finalize(this->GetClockResetModule()); - } - - virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) override; - virtual void Shutdown() override; - virtual void PutToSleep() override; - virtual Result Awaken() override; - virtual Result SwitchToSdr12() override; - virtual Result SetSpeedMode(SpeedMode speed_mode) override; - - virtual SpeedMode GetSpeedMode() const override { - return this->current_speed_mode; - } - - virtual void SetPowerSaving(bool en) override; - virtual void EnableDeviceClock() override; - - virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; - virtual Result IssueStopTransmissionCommand(u32 *out_response) override; - - virtual bool IsSupportedTuning() const override { - return true; - } - - virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override; - virtual void SaveTuningStatusForHs400() override; - - virtual Result GetInternalStatus() const override { - return this->drive_strength_calibration_status; - } - }; - - constexpr inline dd::PhysicalAddress Sdmmc1RegistersPhysicalAddress = UINT64_C(0x700B0000); - - class Sdmmc1Controller : public SdmmcController { - private: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - static constinit inline os::InterruptEventType s_interrupt_event{}; - #endif - - /* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */ - class PowerController { - NON_COPYABLE(PowerController); - NON_MOVEABLE(PowerController); - private: - BusPower current_bus_power; - private: - Result ControlVddioSdmmc1(BusPower bus_power); - void SetSdmmcIoMode(bool is_3_3V); - void ControlRailSdmmc1Io(bool is_power_on); - public: - PowerController(); - ~PowerController(); - - Result PowerOn(BusPower bus_power); - Result PowerOff(); - Result LowerBusPower(); - }; - private: - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - /* TODO: pinmux::PinmuxSession pinmux_session; */ - #endif - BusPower current_bus_power; - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - bool is_pcv_control; - #endif - TYPED_STORAGE(PowerController) power_controller_storage; - PowerController *power_controller; - private: - Result PowerOnForRegisterControl(BusPower bus_power); - void PowerOffForRegisterControl(); - Result LowerBusPowerForRegisterControl(); - void SetSchmittTriggerForRegisterControl(BusPower bus_power); - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - Result PowerOnForPcvControl(BusPower bus_power); - void PowerOffForPcvControl(); - Result LowerBusPowerForPcvControl(); - void SetSchmittTriggerForPcvControl(BusPower bus_power); - #endif - protected: - virtual void SetPad() override { - /* Nothing is needed here. */ - } - - virtual ClockResetController::Module GetClockResetModule() const override { - return ClockResetController::Module_Sdmmc1; - } - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual int GetInterruptNumber() const override { - return 46; - } - - virtual os::InterruptEventType *GetInterruptEvent() const override { - return std::addressof(s_interrupt_event); - } - #endif - - virtual bool IsNeedPeriodicDriveStrengthCalibration() override { - return !IsSocMariko(); - } - - virtual void ClearPadParked() override { - /* Nothing is needed here. */ - } - - virtual Result PowerOn(BusPower bus_power) override; - virtual void PowerOff() override; - virtual Result LowerBusPower() override; - - virtual void SetSchmittTrigger(BusPower bus_power) override; - - virtual u8 GetOutboundTapValue() const override { - if (IsSocMariko()) { - return 0xE; - } else { - return 0x2; - } - } - - virtual u8 GetDefaultInboundTapValue() const override { - if (IsSocMariko()) { - return 0xB; - } else { - return 0x4; - } - } - - virtual u8 GetVrefSelValue() const override { - if (IsSocMariko()) { - return 0x0; - } else { - return 0x7; - } - } - - virtual void SetSlewCodes() override { - if (IsSocMariko()) { - /* Do nothing. */ - } else { - /* Ensure that we can control registers. */ - SdHostStandardController::EnsureControl(); - - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Write the slew values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ - reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 0x1), - APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 0x1)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); - } - } - - virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { - /* Ensure that we can write the offsets. */ - AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); - AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); - - /* Set the offsets. */ - if (IsSocMariko()) { - switch (bus_power) { - case BusPower_1_8V: - *out_auto_cal_pd_offset = 6; - *out_auto_cal_pu_offset = 6; - break; - case BusPower_3_3V: - *out_auto_cal_pd_offset = 0; - *out_auto_cal_pu_offset = 0; - break; - case BusPower_Off: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } else { - switch (bus_power) { - case BusPower_1_8V: - *out_auto_cal_pd_offset = 0x7B; - *out_auto_cal_pu_offset = 0x7B; - break; - case BusPower_3_3V: - *out_auto_cal_pd_offset = 0x7D; - *out_auto_cal_pu_offset = 0; - break; - case BusPower_Off: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - } - - virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { - /* Ensure that we can control registers. */ - SdHostStandardController::EnsureControl(); - - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Determine the drive code values. */ - u8 drvdn, drvup; - if (IsSocMariko()) { - drvdn = 0x8; - drvup = 0x8; - } else { - switch (bus_power) { - case BusPower_1_8V: - drvdn = 0xF; - drvup = 0xB; - break; - case BusPower_3_3V: - drvdn = 0xC; - drvup = 0xC; - break; - case BusPower_Off: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - /* Write the drv up/down values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ - reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, drvdn), - APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, drvup)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); - } - public: - Sdmmc1Controller() : SdmmcController(Sdmmc1RegistersPhysicalAddress) { - this->current_bus_power = BusPower_Off; - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - this->is_pcv_control = false; - #endif - this->power_controller = nullptr; - } - - virtual void Initialize() override; - virtual void Finalize() override; - - void InitializeForRegisterControl(); - void FinalizeForRegisterControl(); - - #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - void InitializeForPcvControl(); - void FinalizeForPcvControl(); - #endif - - virtual bool IsSupportedBusPower(BusPower bus_power) const override { - switch (bus_power) { - case BusPower_Off: return true; - case BusPower_1_8V: return true; - case BusPower_3_3V: return true; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { - switch (bus_width) { - case BusWidth_1Bit: return true; - case BusWidth_4Bit: return true; - case BusWidth_8Bit: return false; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - }; - - class Sdmmc2And4Controller : public SdmmcController { - protected: - virtual bool IsNeedPeriodicDriveStrengthCalibration() override { - return false; - } - - virtual Result PowerOn(BusPower bus_power) override { - /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ - AMS_UNUSED(bus_power); - return ResultSuccess(); - } - - virtual void PowerOff() override { - /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ - } - - virtual Result LowerBusPower() override { - AMS_ABORT("Sdmmc2And4Controller cannot lower bus power\n"); - } - - virtual void SetSchmittTrigger(BusPower bus_power) override { - /* Do nothing. */ - AMS_UNUSED(bus_power); - } - - virtual u8 GetOutboundTapValue() const override { - if (IsSocMariko()) { - return 0xD; - } else { - return 0x8; - } - } - - virtual u8 GetDefaultInboundTapValue() const override { - if (IsSocMariko()) { - return 0xB; - } else { - return 0x0; - } - } - - virtual u8 GetVrefSelValue() const override { - return 0x7; - } - - virtual void SetSlewCodes() override { - /* Do nothing. */ - } - - virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { - /* Ensure that we can write the offsets. */ - AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); - AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); - - /* Sdmmc2And4Controller only supports 1.8v. */ - AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); - - /* Set the offsets. */ - *out_auto_cal_pd_offset = 5; - *out_auto_cal_pu_offset = 5; - } - public: - explicit Sdmmc2And4Controller(dd::PhysicalAddress registers_phys_addr) : SdmmcController(registers_phys_addr) { - /* ... */ - } - - virtual bool IsSupportedBusPower(BusPower bus_power) const override { - switch (bus_power) { - case BusPower_Off: return true; - case BusPower_1_8V: return true; - case BusPower_3_3V: return false; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { - switch (bus_width) { - case BusWidth_1Bit: return true; - case BusWidth_4Bit: return true; - case BusWidth_8Bit: return true; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - }; - - constexpr inline dd::PhysicalAddress Sdmmc2RegistersPhysicalAddress = UINT64_C(0x700B0200); - - class Sdmmc2Controller : public Sdmmc2And4Controller { - private: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - static constinit inline os::InterruptEventType s_interrupt_event{}; - #endif - protected: - virtual void SetPad() override { - /* Nothing is needed here. */ - } - - virtual ClockResetController::Module GetClockResetModule() const override { - return ClockResetController::Module_Sdmmc2; - } - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual int GetInterruptNumber() const override { - return 47; - } - - virtual os::InterruptEventType *GetInterruptEvent() const override { - return std::addressof(s_interrupt_event); - } - #endif - - virtual void ClearPadParked() override { - if (IsSocMariko()) { - /* Nothing is needed here. */ - } else { - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Clear all MISC2PMC_EMMC2_*_PARK bits. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 0)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); - } - } - - virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { - /* SDMMC2 only supports 1.8v. */ - AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); - - /* Ensure that we can control registers. */ - SdHostStandardController::EnsureControl(); - - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - if (IsSocMariko()) { - /* Write the drv up/down values to APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL. */ - reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 0xA), - APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 0xA)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL); - } else { - /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 0x10), - APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 0x10)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); - } - } - public: - Sdmmc2Controller() : Sdmmc2And4Controller(Sdmmc2RegistersPhysicalAddress) { - /* ... */ - } - }; - - constexpr inline dd::PhysicalAddress Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600); - - class Sdmmc4Controller : public Sdmmc2And4Controller { - private: - #if defined(AMS_SDMMC_USE_OS_EVENTS) - static constinit inline os::InterruptEventType s_interrupt_event{}; - #endif - protected: - virtual void SetPad() override { - if (IsSocMariko()) { - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Enable Schmitt Trigger in emmc4 iobrick. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, ENABLE)); - - /* Clear CMD_PULLU, CLK_PULLD, DQS_PULLD. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 0), - APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 0), - APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 0)); - - /* Read again to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL); - } else { - /* On Erista, we can just leave the reset value intact. */ - } - } - - virtual ClockResetController::Module GetClockResetModule() const override { - return ClockResetController::Module_Sdmmc4; - } - - #if defined(AMS_SDMMC_USE_OS_EVENTS) - virtual int GetInterruptNumber() const override { - return 63; - } - - virtual os::InterruptEventType *GetInterruptEvent() const override { - return std::addressof(s_interrupt_event); - } - #endif - - virtual void ClearPadParked() override { - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Clear all MISC2PMC_EMMC4_*_PARK bits. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 0)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); - } - - virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { - /* SDMMC4 only supports 1.8v. */ - AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); - - /* Ensure that we can control registers. */ - SdHostStandardController::EnsureControl(); - - /* Get the apb registers address. */ - const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); - - /* Determine the drv up/down values. */ - u8 drvdn, drvup; - if (IsSocMariko()) { - drvdn = 0xA; - drvup = 0xA; - } else { - drvdn = 0x10; - drvup = 0x10; - } - - /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ - reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, drvdn), - APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, drvup)); - - /* Read to be sure our config takes. */ - reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); - } - public: - Sdmmc4Controller() : Sdmmc2And4Controller(Sdmmc4RegistersPhysicalAddress) { - /* ... */ - } - }; - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp deleted file mode 100644 index 9d3b943..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - - #include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" - - namespace ams::sdmmc::impl { - - using SdmmcControllerForPortSdCard0 = Sdmmc1Controller; - using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller; - using SdmmcControllerForPortMmc0 = Sdmmc4Controller; - - } - -#else - #error "Unknown board for ams::sdmmc::SdmmcController" -#endif diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp deleted file mode 100644 index ae90332..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "sdmmc_timer.hpp" - -namespace ams::sdmmc::impl { - - namespace { - - #if defined(AMS_SDMMC_USE_OS_TIMER) - void SpinWaitMicroSeconds(u32 us) { - const os::Tick timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMicroSeconds(us)) + os::Tick(1); - while (true) { - if (os::GetSystemTick() > timeout_tick) { - break; - } - } - } - - ALWAYS_INLINE void DataSynchronizationBarrier() { - #if defined(ATMOSPHERE_ARCH_ARM64) - __asm__ __volatile__("dsb sy" ::: "memory"); - #elif defined(ATMOSPHERE_ARCH_ARM) - __asm__ __volatile__("dsb" ::: "memory"); - #else - #error "Unknown architecture for DataSynchronizationBarrier" - #endif - } - - ALWAYS_INLINE void InstructionSynchronizationBarrier() { - #if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM) - __asm__ __volatile__("isb" ::: "memory"); - #else - #error "Unknown architecture for InstructionSynchronizationBarrier" - #endif - } - #endif - - } - - void WaitMicroSeconds(u32 us) { - #if defined(AMS_SDMMC_USE_OS_TIMER) - /* Ensure that nothing is reordered before we wait. */ - DataSynchronizationBarrier(); - InstructionSynchronizationBarrier(); - - /* If the time is small, spinloop, otherwise pend ourselves. */ - if (us < 100) { - SpinWaitMicroSeconds(us); - } else { - os::SleepThread(TimeSpan::FromMicroSeconds(us)); - } - - /* Ensure that nothing is reordered after we wait. */ - DataSynchronizationBarrier(); - InstructionSynchronizationBarrier(); - #elif defined(AMS_SDMMC_USE_UTIL_TIMER) - util::WaitMicroSeconds(us); - #else - #error "Unknown context for ams::sdmmc::impl::WaitMicroSeconds" - #endif - } - - void WaitClocks(u32 num_clocks, u32 clock_frequency_khz) { - AMS_ABORT_UNLESS(clock_frequency_khz > 0); - WaitMicroSeconds(util::DivideUp(1000 * num_clocks, clock_frequency_khz)); - } - -} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp deleted file mode 100644 index 5d81495..0000000 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 . - */ -#pragma once -#include - -namespace ams::sdmmc::impl { - - void WaitMicroSeconds(u32 us); - void WaitClocks(u32 num_clocks, u32 clock_frequency_khz); - - #if defined(AMS_SDMMC_USE_OS_TIMER) - class ManualTimer { - private: - os::Tick timeout_tick; - bool is_timed_out; - public: - explicit ManualTimer(u32 ms) { - this->timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(ms)); - this->is_timed_out = false; - } - - bool Update() { - if (this->is_timed_out) { - return false; - } - - this->is_timed_out = os::GetSystemTick() > this->timeout_tick; - return true; - } - }; - #elif defined(AMS_SDMMC_USE_UTIL_TIMER) - class ManualTimer { - private: - u32 timeout_us; - bool is_timed_out; - public: - explicit ManualTimer(u32 ms) { - this->timeout_us = util::GetMicroSeconds() + (ms * 1000); - this->is_timed_out = false; - } - - bool Update() { - if (this->is_timed_out) { - return false; - } - - this->is_timed_out = util::GetMicroSeconds() > this->timeout_us; - return true; - } - }; - #else - #error "Unknown context for ams::sdmmc::ManualTimer" - #endif - -} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_common.cpp b/libraries/libvapours/source/sdmmc/sdmmc_common.cpp deleted file mode 100644 index 598e048..0000000 --- a/libraries/libvapours/source/sdmmc/sdmmc_common.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "impl/sdmmc_i_host_controller.hpp" -#include "impl/sdmmc_i_device_accessor.hpp" -#include "impl/sdmmc_clock_reset_controller.hpp" -#include "impl/sdmmc_port_mmc0.hpp" -#include "impl/sdmmc_port_sd_card0.hpp" -#include "impl/sdmmc_port_gc_asic0.hpp" - -namespace ams::sdmmc { - - namespace { - - impl::IHostController *GetHostController(Port port) { - /* Get the controller. */ - impl::IHostController *host_controller = nullptr; - switch (port) { - case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break; - case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break; - case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure it's valid */ - AMS_ABORT_UNLESS(host_controller != nullptr); - return host_controller; - } - - impl::IDeviceAccessor *GetDeviceAccessor(Port port) { - /* Get the accessor. */ - impl::IDeviceAccessor *device_accessor = nullptr; - switch (port) { - case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break; - case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break; - case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure it's valid */ - AMS_ABORT_UNLESS(device_accessor != nullptr); - return device_accessor; - } - - } - - - - void Initialize(Port port) { - return GetDeviceAccessor(port)->Initialize(); - } - - void Finalize(Port port) { - return GetDeviceAccessor(port)->Finalize(); - } - -#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) - void SwitchToPcvClockResetControl() { - return impl::ClockResetController::SwitchToPcvControl(); - } -#endif - -#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) - void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - return GetDeviceAccessor(port)->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); - } - - void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { - return GetDeviceAccessor(port)->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); - } -#endif - - void ChangeCheckTransferInterval(Port port, u32 ms) { - return GetHostController(port)->ChangeCheckTransferInterval(ms); - } - void SetDefaultCheckTransferInterval(Port port) { - return GetHostController(port)->SetDefaultCheckTransferInterval(); - } - - Result Activate(Port port) { - return GetDeviceAccessor(port)->Activate(); - } - - void Deactivate(Port port) { - return GetDeviceAccessor(port)->Deactivate(); - } - - Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors) { - return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, dst, dst_size, true); - } - - Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size) { - return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, const_cast(src), src_size, false); - } - - Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { - return GetDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); - } - - Result GetDeviceSpeedMode(SpeedMode *out, Port port) { - return GetDeviceAccessor(port)->GetSpeedMode(out); - } - - Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port) { - return GetDeviceAccessor(port)->GetMemoryCapacity(out_num_sectors); - } - - Result GetDeviceStatus(u32 *out_device_status, Port port) { - return GetDeviceAccessor(port)->GetDeviceStatus(out_device_status); - } - - Result GetDeviceCid(void *out, size_t out_size, Port port) { - return GetDeviceAccessor(port)->GetCid(out, out_size); - } - - Result GetDeviceCsd(void *out, size_t out_size, Port port) { - return GetDeviceAccessor(port)->GetCsd(out, out_size); - } - - void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port) { - return GetDeviceAccessor(port)->GetAndClearErrorInfo(out_error_info, out_log_size, out_log_buffer, log_buffer_size); - } - -} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp b/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp deleted file mode 100644 index 2071fd3..0000000 --- a/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "impl/sdmmc_gc_asic_device_accessor.hpp" -#include "impl/sdmmc_port_mmc0.hpp" -#include "impl/sdmmc_port_sd_card0.hpp" -#include "impl/sdmmc_port_gc_asic0.hpp" - -namespace ams::sdmmc { - - namespace { - - impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) { - /* Get the accessor. */ - impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr; - switch (port) { - case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure it's valid */ - AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr); - return gc_asic_device_accessor; - } - - } - - void PutGcAsicToSleep(Port port) { - return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep(); - } - - Result AwakenGcAsic(Port port) { - return GetGcAsicDeviceAccessor(port)->AwakenGcAsic(); - } - - Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) { - return GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size); - } - - Result FinishGcAsicOperation(Port port) { - return GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation(); - } - - Result AbortGcAsicOperation(Port port) { - return GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation(); - } - - Result SleepGcAsic(Port port) { - return GetGcAsicDeviceAccessor(port)->SleepGcAsic(); - } - - Result UpdateGcAsicKey(Port port) { - return GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey(); - } - - void SignalGcRemovedEvent(Port port) { - return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent(); - } - - void ClearGcRemovedEvent(Port port) { - return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent(); - } - -} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp b/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp deleted file mode 100644 index 76fc571..0000000 --- a/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "impl/sdmmc_mmc_device_accessor.hpp" -#include "impl/sdmmc_port_mmc0.hpp" -#include "impl/sdmmc_port_sd_card0.hpp" -#include "impl/sdmmc_port_gc_asic0.hpp" - -namespace ams::sdmmc { - - namespace { - - impl::MmcDeviceAccessor *GetMmcDeviceAccessor(Port port) { - /* Get the accessor. */ - impl::MmcDeviceAccessor *mmc_device_accessor = nullptr; - switch (port) { - case Port_Mmc0: mmc_device_accessor = impl::GetMmcDeviceAccessorOfPortMmc0(); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure it's valid */ - AMS_ABORT_UNLESS(mmc_device_accessor != nullptr); - return mmc_device_accessor; - } - - } - - void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size) { - return GetMmcDeviceAccessor(port)->SetMmcWorkBuffer(buffer, buffer_size); - } - - void PutMmcToSleep(Port port) { - return GetMmcDeviceAccessor(port)->PutMmcToSleep(); - } - - void AwakenMmc(Port port) { - return GetMmcDeviceAccessor(port)->AwakenMmc(); - } - - Result SelectMmcPartition(Port port, MmcPartition mmc_partition) { - return GetMmcDeviceAccessor(port)->SelectMmcPartition(mmc_partition); - } - - Result EraseMmc(Port port) { - return GetMmcDeviceAccessor(port)->EraseMmc(); - } - - Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port) { - return GetMmcDeviceAccessor(port)->GetMmcBootPartitionCapacity(out_num_sectors); - } - - Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port) { - return GetMmcDeviceAccessor(port)->GetMmcExtendedCsd(out_buffer, buffer_size); - } - - Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { - return GetMmcDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); - } - -} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp b/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp deleted file mode 100644 index c10a38d..0000000 --- a/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 . - */ -#if defined(ATMOSPHERE_IS_STRATOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_MESOSPHERE) -#include -#elif defined(ATMOSPHERE_IS_EXOSPHERE) -#include -#else -#include -#endif -#include "impl/sdmmc_sd_card_device_accessor.hpp" -#include "impl/sdmmc_port_mmc0.hpp" -#include "impl/sdmmc_port_sd_card0.hpp" -#include "impl/sdmmc_port_gc_asic0.hpp" - -namespace ams::sdmmc { - - namespace { - - impl::SdCardDeviceAccessor *GetSdCardDeviceAccessor(Port port) { - /* Get the accessor. */ - impl::SdCardDeviceAccessor *sd_card_device_accessor = nullptr; - switch (port) { - case Port_SdCard0: sd_card_device_accessor = impl::GetSdCardDeviceAccessorOfPortSdCard0(); break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure it's valid */ - AMS_ABORT_UNLESS(sd_card_device_accessor != nullptr); - return sd_card_device_accessor; - } - - } - - void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size) { - return GetSdCardDeviceAccessor(port)->SetSdCardWorkBuffer(buffer, buffer_size); - } - - void PutSdCardToSleep(Port port) { - return GetSdCardDeviceAccessor(port)->PutSdCardToSleep(); - } - - void AwakenSdCard(Port port) { - return GetSdCardDeviceAccessor(port)->AwakenSdCard(); - } - - Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port) { - return GetSdCardDeviceAccessor(port)->GetSdCardProtectedAreaCapacity(out_num_sectors); - } - - Result GetSdCardScr(void *dst, size_t dst_size, Port port) { - return GetSdCardDeviceAccessor(port)->GetSdCardScr(dst, dst_size); - } - - Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function) { - return GetSdCardDeviceAccessor(port)->GetSdCardSwitchFunctionStatus(dst, dst_size, switch_function); - } - - Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode) { - return GetSdCardDeviceAccessor(port)->GetSdCardCurrentConsumption(out_current_consumption, speed_mode); - } - - Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port) { - return GetSdCardDeviceAccessor(port)->GetSdCardSdStatus(dst, dst_size); - } - - Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { - return GetSdCardDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); - } - - bool IsSdCardInserted(Port port) { - return GetSdCardDeviceAccessor(port)->IsSdCardInserted(); - } - - bool IsSdCardRemoved(Port port) { - return GetSdCardDeviceAccessor(port)->IsSdCardRemoved(); - } - - - void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg) { - return GetSdCardDeviceAccessor(port)->RegisterSdCardDetectionEventCallback(callback, arg); - } - - void UnregisterSdCardDetectionEventCallback(Port port) { - return GetSdCardDeviceAccessor(port)->UnregisterSdCardDetectionEventCallback(); - } - -} -