/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 *m_registers;
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
BufferInfo m_buffer_infos[NumBufferInfos];
#endif
#if defined(AMS_SDMMC_USE_OS_EVENTS)
os::MultiWaitType m_multi_wait;
os::InterruptEventType *m_interrupt_event;
os::MultiWaitHolderType m_interrupt_event_holder;
os::EventType *m_removed_event;
os::MultiWaitHolderType m_removed_event_holder;
#endif
u64 m_next_sdma_address;
u32 m_check_transfer_interval_ms;
u32 m_device_clock_frequency_khz;
bool m_is_power_saving_enable;
bool m_is_device_clock_enable;
ResponseType m_last_response_type;
u32 m_last_response[4];
u32 m_last_stop_transmission_response;
protected:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
void PreSetInterruptEvent(os::InterruptEventType *ie) {
m_interrupt_event = ie;
}
bool IsRemoved() const {
return m_removed_event != nullptr && os::TryWaitEvent(m_removed_event);
}
#endif
void SetDeviceClockFrequencyKHz(u32 khz) {
m_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
R_SUCCEED();
}
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 {
m_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 m_device_clock_frequency_khz;
}
virtual void SetPowerSaving(bool en) override;
virtual bool IsPowerSavingEnable() const override {
return m_is_power_saving_enable;
}
virtual void EnableDeviceClock() override;
virtual void DisableDeviceClock() override;
virtual bool IsDeviceClockEnable() const override {
return m_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) override {
AMS_UNUSED(speed_mode, command_index);
AMS_ABORT("Tuning not supported\n");
}
virtual void SaveTuningStatusForHs400() override {
AMS_ABORT("SaveTuningStatusForHs400 not supported\n");
}
};
}