diff --git a/libraries/libmesosphere/Makefile b/libraries/libmesosphere/Makefile
index 7e8c26d..18027ae 100644
--- a/libraries/libmesosphere/Makefile
+++ b/libraries/libmesosphere/Makefile
@@ -16,7 +16,7 @@
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -flto
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
-SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source)
+SOURCES += $(foreach v,$(call ALL_SOURCE_DIRS,../libvapours/source),$(if $(findstring ../libvapours/source/sdmmc,$v),,$v))
LIBS :=
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
deleted file mode 100644
index c9234e9..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_base_device_accessor.hpp
deleted file mode 100644
index 8cb0490..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 1790380..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_clock_reset_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_clock_reset_controller.hpp
deleted file mode 100644
index 29ac276..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 8d8c39d..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 85144bf..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 3a49428..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 4043037..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_device_detector.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.cpp
deleted file mode 100644
index 3529b85..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_device_detector.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_device_detector.hpp
deleted file mode 100644
index a61f050..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index a1ebe02..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index c6c24ec..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_device_accessor.hpp
deleted file mode 100644
index 94e85bd..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_i_host_controller.hpp
deleted file mode 100644
index f4439bb..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index bf36180..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 89b6230..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.cpp
deleted file mode 100644
index 7468aab..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_mmc_device_accessor.hpp
deleted file mode 100644
index 97ac864..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_gc_asic0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.cpp
deleted file mode 100644
index 763869d..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_gc_asic0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_gc_asic0.hpp
deleted file mode 100644
index 8dc7880..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.cpp
deleted file mode 100644
index 30cfc86..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_mmc0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_mmc0.hpp
deleted file mode 100644
index b675bd4..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_sd_card0.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.cpp
deleted file mode 100644
index 5288a5e..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_port_sd_card0.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_port_sd_card0.hpp
deleted file mode 100644
index 690dbb6..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 9bfff1a..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 4f37b0c..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index f03d153..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 7bdf0a3..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index e104374..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index 3b16008..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/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
deleted file mode 100644
index d007d9d..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_select_sdmmc_controller.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_select_sdmmc_controller.hpp
deleted file mode 100644
index 9d3b943..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_timer.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.cpp
deleted file mode 100644
index ae90332..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/impl/sdmmc_timer.hpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/impl/sdmmc_timer.hpp
deleted file mode 100644
index 5d81495..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/sdmmc_common.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_common.cpp
deleted file mode 100644
index 598e048..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/sdmmc_gc_asic.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_gc_asic.cpp
deleted file mode 100644
index 2071fd3..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/sdmmc_mmc.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_mmc.cpp
deleted file mode 100644
index 76fc571..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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/board/nintendo/nx/sdmmc_sd_card.cpp b/libraries/libvapours/source/sdmmc/board/nintendo/nx/sdmmc_sd_card.cpp
deleted file mode 100644
index c10a38d..0000000
--- a/libraries/libvapours/source/sdmmc/board/nintendo/nx/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();
- }
-
-}
-
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp
new file mode 100644
index 0000000..c9234e9
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp
new file mode 100644
index 0000000..8cb0490
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp
new file mode 100644
index 0000000..1790380
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_clock_reset_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp
new file mode 100644
index 0000000..29ac276
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/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
new file mode 100644
index 0000000..8d8c39d
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/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
new file mode 100644
index 0000000..85144bf
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/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
new file mode 100644
index 0000000..3a49428
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/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
new file mode 100644
index 0000000..4043037
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_device_detector.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp
new file mode 100644
index 0000000..3529b85
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_device_detector.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp
new file mode 100644
index 0000000..a61f050
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_gc_asic_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp
new file mode 100644
index 0000000..a1ebe02
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_gc_asic_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp
new file mode 100644
index 0000000..c6c24ec
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp
new file mode 100644
index 0000000..94e85bd
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/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