mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 11:15:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			683 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			683 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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 <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #pragma once
 | |
| #include <vapours.hpp>
 | |
| #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 *m_sdmmc_registers;
 | |
|             bool m_is_shutdown;
 | |
|             bool m_is_awake;
 | |
|             SpeedMode m_current_speed_mode;
 | |
|             BusPower m_bus_power_before_sleep;
 | |
|             BusWidth m_bus_width_before_sleep;
 | |
|             SpeedMode m_speed_mode_before_sleep;
 | |
|             u8 m_tap_value_before_sleep;
 | |
|             bool m_is_powersaving_enable_before_sleep;
 | |
|             u8 m_tap_value_for_hs_400;
 | |
|             bool m_is_valid_tap_value_for_hs_400;
 | |
|             Result m_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(AMS_OFFSETOF(SdmmcRegisters, sd_host_standard_registers) == 0);
 | |
|                 m_sdmmc_registers = reinterpret_cast<SdmmcRegisters *>(m_registers);
 | |
| 
 | |
|                 m_is_shutdown                        = true;
 | |
|                 m_is_awake                           = true;
 | |
|                 m_is_valid_tap_value_for_hs_400      = false;
 | |
|                 m_drive_strength_calibration_status  = sdmmc::ResultDriveStrengthCalibrationNotCompleted();
 | |
|                 m_tap_value_for_hs_400               = 0;
 | |
|                 m_current_speed_mode                 = SpeedMode_MmcIdentification;
 | |
|                 m_bus_power_before_sleep             = BusPower_Off;
 | |
|                 m_bus_width_before_sleep             = BusWidth_1Bit;
 | |
|                 m_speed_mode_before_sleep            = SpeedMode_MmcIdentification;
 | |
|                 m_tap_value_before_sleep             = 0;
 | |
|                 m_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 m_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 m_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 m_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 m_pinmux_session; */
 | |
|             #endif
 | |
|             BusPower m_current_bus_power;
 | |
|             #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
 | |
|             bool m_is_pcv_control;
 | |
|             #endif
 | |
|             util::TypedStorage<PowerController> m_power_controller_storage;
 | |
|             PowerController *m_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) {
 | |
|                 m_current_bus_power = BusPower_Off;
 | |
|                 #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
 | |
|                 m_is_pcv_control = false;
 | |
|                 #endif
 | |
|                 m_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) {
 | |
|                 /* ... */
 | |
|             }
 | |
|     };
 | |
| 
 | |
| }
 |