diff --git a/stratosphere/boot/Makefile b/stratosphere/boot/Makefile index 0fcdbf618..6bf16b0bd 100644 --- a/stratosphere/boot/Makefile +++ b/stratosphere/boot/Makefile @@ -33,7 +33,7 @@ endef #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source source/i2c_driver source/updater +SOURCES := source source/i2c source/i2c/driver source/i2c/driver/impl source/updater DATA := data INCLUDES := include ../../common/include EXEFS_SRC := exefs_src diff --git a/stratosphere/boot/source/boot_battery_driver.hpp b/stratosphere/boot/source/boot_battery_driver.hpp index fa69afbc8..27cbd0b5b 100644 --- a/stratosphere/boot/source/boot_battery_driver.hpp +++ b/stratosphere/boot/source/boot_battery_driver.hpp @@ -18,21 +18,21 @@ #include #include -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" #include "boot_battery_parameters.hpp" class BatteryDriver { private: - I2cSessionImpl i2c_session; + sts::i2c::driver::Session i2c_session; public: BatteryDriver() { - I2cDriver::Initialize(); - I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max17050); + sts::i2c::driver::Initialize(); + sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050); } ~BatteryDriver() { - I2cDriver::CloseSession(this->i2c_session); - I2cDriver::Finalize(); + sts::i2c::driver::CloseSession(this->i2c_session); + sts::i2c::driver::Finalize(); } private: static const Max17050Parameters *GetBatteryParameters(); diff --git a/stratosphere/boot/source/boot_charger_driver.hpp b/stratosphere/boot/source/boot_charger_driver.hpp index 19296748f..b16050839 100644 --- a/stratosphere/boot/source/boot_charger_driver.hpp +++ b/stratosphere/boot/source/boot_charger_driver.hpp @@ -18,7 +18,7 @@ #include #include -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" #include "boot_functions.hpp" #include "boot_bq24193_charger.hpp" @@ -26,19 +26,19 @@ class ChargerDriver { private: static constexpr u32 GpioPadName_Bq24193Charger = 0xA; private: - I2cSessionImpl i2c_session; + sts::i2c::driver::Session i2c_session; public: ChargerDriver() { - I2cDriver::Initialize(); - I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Bq24193); + sts::i2c::driver::Initialize(); + sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193); Boot::GpioConfigure(GpioPadName_Bq24193Charger); Boot::GpioSetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output); } ~ChargerDriver() { - I2cDriver::CloseSession(this->i2c_session); - I2cDriver::Finalize(); + sts::i2c::driver::CloseSession(this->i2c_session); + sts::i2c::driver::Finalize(); } private: Result Read(u8 addr, u8 *out_data); diff --git a/stratosphere/boot/source/boot_check_clock.cpp b/stratosphere/boot/source/boot_check_clock.cpp index e8561ca94..7d27deaaf 100644 --- a/stratosphere/boot/source/boot_check_clock.cpp +++ b/stratosphere/boot/source/boot_check_clock.cpp @@ -28,11 +28,7 @@ static constexpr u32 ExpectedUtmipVal = (ExpectedUtmipDivN | ExpectedUtmipDivM) static constexpr u32 ExpectedUtmipMask = 0xFFFF00; static bool IsUsbClockValid() { - u64 _vaddr; - if (R_FAILED(svcQueryIoMapping(&_vaddr, 0x60006000ul, 0x1000))) { - std::abort(); - } - volatile u32 *car_regs = reinterpret_cast(_vaddr); + volatile u32 *car_regs = reinterpret_cast(GetIoMapping(0x60006000ul, 0x1000)); const u32 pllu = car_regs[0xC0 >> 2]; const u32 utmip = car_regs[0x480 >> 2]; diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 53ff13f76..426e19ac0 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -16,7 +16,7 @@ #include "boot_functions.hpp" #include "boot_display_config.hpp" -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" /* Helpful defines. */ constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; @@ -56,16 +56,6 @@ static uintptr_t g_gpio_regs = 0; static uintptr_t g_apb_misc_regs = 0; static uintptr_t g_mipi_cal_regs = 0; -static inline uintptr_t QueryVirtualAddress(uintptr_t phys, size_t size) { - uintptr_t aligned_phys = phys & ~0xFFFul; - size_t aligned_size = size + (phys - aligned_phys); - uintptr_t aligned_virt; - if (R_FAILED(svcQueryIoMapping(&aligned_virt, aligned_phys, aligned_size))) { - std::abort(); - } - return aligned_virt + (phys - aligned_phys); -} - static inline void WriteRegister(volatile u32 *reg, u32 val) { *reg = val; } @@ -108,12 +98,12 @@ static inline void ReadWriteRegisterBits(uintptr_t reg, u32 val, u32 mask) { } static void InitializeRegisterBaseAddresses() { - g_disp1_regs = QueryVirtualAddress(Disp1Base, Disp1Size); - g_dsi_regs = QueryVirtualAddress(DsiBase, DsiSize); - g_clk_rst_regs = QueryVirtualAddress(ClkRstBase, ClkRstSize); - g_gpio_regs = QueryVirtualAddress(GpioBase, GpioSize); - g_apb_misc_regs = QueryVirtualAddress(ApbMiscBase, ApbMiscSize); - g_mipi_cal_regs = QueryVirtualAddress(MipiCalBase, MipiCalSize); + g_disp1_regs = GetIoMapping(Disp1Base, Disp1Size); + g_dsi_regs = GetIoMapping(DsiBase, DsiSize); + g_clk_rst_regs = GetIoMapping(ClkRstBase, ClkRstSize); + g_gpio_regs = GetIoMapping(GpioBase, GpioSize); + g_apb_misc_regs = GetIoMapping(ApbMiscBase, ApbMiscSize); + g_mipi_cal_regs = GetIoMapping(MipiCalBase, MipiCalSize); } static inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) { @@ -232,17 +222,17 @@ void Boot::InitializeDisplay() { /* Turn on DSI/voltage rail. */ { - I2cSessionImpl i2c_session; - I2cDriver::Initialize(); - I2cDriver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic); + sts::i2c::driver::Session i2c_session; + sts::i2c::driver::Initialize(); + ON_SCOPE_EXIT { sts::i2c::driver::Finalize(); }; + + sts::i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic); if (g_is_mariko) { Boot::WriteI2cRegister(i2c_session, 0x18, 0x3A); Boot::WriteI2cRegister(i2c_session, 0x1F, 0x71); } Boot::WriteI2cRegister(i2c_session, 0x23, 0xD0); - - I2cDriver::Finalize(); } /* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */ @@ -449,7 +439,7 @@ void Boot::FinalizeDisplay() { /* Nintendo waits 5 frames before continuing. */ { - const uintptr_t host1x_vaddr = QueryVirtualAddress(0x500030a4, 4); + const uintptr_t host1x_vaddr = GetIoMapping(0x500030a4, 4); const u32 start_val = ReadRegister(host1x_vaddr); while (ReadRegister(host1x_vaddr) < start_val + 5) { /* spinlock here. */ diff --git a/stratosphere/boot/source/boot_functions.hpp b/stratosphere/boot/source/boot_functions.hpp index 001962b6d..b1c743e69 100644 --- a/stratosphere/boot/source/boot_functions.hpp +++ b/stratosphere/boot/source/boot_functions.hpp @@ -19,7 +19,8 @@ #include #include "boot_types.hpp" -#include "i2c_driver/i2c_types.hpp" +#include "i2c/i2c_types.hpp" +#include "i2c/driver/i2c_api.hpp" class Boot { public: @@ -67,9 +68,9 @@ class Boot { static bool IsMariko(); /* I2C Utilities. */ - static Result ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); - static Result WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); - static Result WriteI2cRegister(I2cSessionImpl &session, const u8 address, const u8 value); + static Result ReadI2cRegister(sts::i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); + static Result WriteI2cRegister(sts::i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); + static Result WriteI2cRegister(sts::i2c::driver::Session &session, const u8 address, const u8 value); /* Splash Screen/Display utilities. */ static void InitializeDisplay(); diff --git a/stratosphere/boot/source/boot_gpio_utils.cpp b/stratosphere/boot/source/boot_gpio_utils.cpp index 352ca2428..15fcc6996 100644 --- a/stratosphere/boot/source/boot_gpio_utils.cpp +++ b/stratosphere/boot/source/boot_gpio_utils.cpp @@ -30,11 +30,7 @@ static inline u32 GetGpioPadDescriptor(u32 gpio_pad_name) { static uintptr_t GetGpioBaseAddress() { if (!g_initialized_gpio_vaddr) { - u64 vaddr; - if (R_FAILED(svcQueryIoMapping(&vaddr, Boot::GpioPhysicalBase, 0x1000))) { - std::abort(); - } - g_gpio_vaddr = vaddr; + g_gpio_vaddr = GetIoMapping(Boot::GpioPhysicalBase, 0x1000); g_initialized_gpio_vaddr = true; } return g_gpio_vaddr; diff --git a/stratosphere/boot/source/boot_i2c_utils.cpp b/stratosphere/boot/source/boot_i2c_utils.cpp index f42276add..77986c735 100644 --- a/stratosphere/boot/source/boot_i2c_utils.cpp +++ b/stratosphere/boot/source/boot_i2c_utils.cpp @@ -15,7 +15,7 @@ */ #include "boot_functions.hpp" -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" template static Result RetryUntilSuccess(F f) { @@ -35,21 +35,21 @@ static Result RetryUntilSuccess(F f) { } } -Result Boot::ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { +Result Boot::ReadI2cRegister(sts::i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) { std::abort(); } - u8 cmd_list[I2cCommandListFormatter::MaxCommandListSize]; + u8 cmd_list[sts::i2c::CommandListFormatter::MaxCommandListSize]; - I2cCommandListFormatter formatter(cmd_list, sizeof(cmd_list)); + sts::i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list)); R_ASSERT(formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size)); R_ASSERT(formatter.EnqueueReceiveCommand(static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size)); - return RetryUntilSuccess([&]() { return I2cDriver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); }); + return RetryUntilSuccess([&]() { return sts::i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); }); } -Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { +Result Boot::WriteI2cRegister(sts::i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) { std::abort(); } @@ -60,9 +60,9 @@ Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src std::memcpy(&cmd_list[0], cmd, cmd_size); std::memcpy(&cmd_list[cmd_size], src, src_size); - return RetryUntilSuccess([&]() { return I2cDriver::Send(session, cmd_list, src_size + cmd_size, static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop)); }); + return RetryUntilSuccess([&]() { return sts::i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop)); }); } -Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 address, const u8 value) { +Result Boot::WriteI2cRegister(sts::i2c::driver::Session &session, const u8 address, const u8 value) { return Boot::WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address)); } diff --git a/stratosphere/boot/source/boot_pcv.cpp b/stratosphere/boot/source/boot_pcv.cpp new file mode 100644 index 000000000..90108b3ca --- /dev/null +++ b/stratosphere/boot/source/boot_pcv.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c/i2c_types.hpp" +#include "i2c/driver/impl/i2c_pcv.hpp" +#include "i2c/driver/impl/i2c_registers.hpp" + +using namespace sts::i2c::driver::impl; + +namespace sts::pcv { + + void Initialize() { + /* Don't do anything. */ + } + + void Finalize() { + /* Don't do anything. */ + } + + Result SetClockRate(PcvModule module, u32 hz) { + /* Get clock/reset registers. */ + ClkRstRegisters regs; + regs.SetBus(ConvertFromPcvModule(module)); + /* Set clock enabled/source. */ + SetRegisterBits(regs.clk_en_reg, regs.mask); + ReadWriteRegisterBits(regs.clk_src_reg, 0x4, 0xFF); + svcSleepThread(1000ul); + ReadWriteRegisterBits(regs.clk_src_reg, 0, 0xE0000000); + svcSleepThread(2000ul); + + return ResultSuccess; + } + + Result SetClockEnabled(PcvModule module, bool enabled) { + return ResultSuccess; + } + + Result SetVoltageEnabled(u32 domain, bool enabled) { + return ResultSuccess; + } + + Result SetVoltageValue(u32 domain, u32 voltage) { + return ResultSuccess; + } + + Result SetReset(PcvModule module, bool reset) { + /* Get clock/reset registers. */ + ClkRstRegisters regs; + regs.SetBus(ConvertFromPcvModule(module)); + + /* Set/clear reset. */ + if (reset) { + SetRegisterBits(regs.rst_reg, regs.mask); + } else { + ClearRegisterBits(regs.rst_reg, ~regs.mask); + } + + return ResultSuccess; + } + +} diff --git a/stratosphere/boot/source/boot_pinmux_utils.cpp b/stratosphere/boot/source/boot_pinmux_utils.cpp index 754e9fb07..0a00a38c6 100644 --- a/stratosphere/boot/source/boot_pinmux_utils.cpp +++ b/stratosphere/boot/source/boot_pinmux_utils.cpp @@ -38,11 +38,7 @@ static inline const PinmuxDrivePadDefinition *GetPinmuxDrivePadDefinition(u32 pi static uintptr_t GetPinmuxBaseAddress() { if (!g_initialized_pinmux_vaddr) { - u64 vaddr; - if (R_FAILED(svcQueryIoMapping(&vaddr, Boot::ApbMiscPhysicalBase, 0x4000))) { - std::abort(); - } - g_pinmux_vaddr = vaddr; + g_pinmux_vaddr = GetIoMapping(Boot::ApbMiscPhysicalBase, 0x4000); g_initialized_pinmux_vaddr = true; } return g_pinmux_vaddr; diff --git a/stratosphere/boot/source/boot_pmic_driver.hpp b/stratosphere/boot/source/boot_pmic_driver.hpp index 161fd39b5..a41f4b13f 100644 --- a/stratosphere/boot/source/boot_pmic_driver.hpp +++ b/stratosphere/boot/source/boot_pmic_driver.hpp @@ -18,21 +18,21 @@ #include #include -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" #include "boot_battery_driver.hpp" class PmicDriver { private: - I2cSessionImpl i2c_session; + sts::i2c::driver::Session i2c_session; public: PmicDriver() { - I2cDriver::Initialize(); - I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic); + sts::i2c::driver::Initialize(); + sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic); } ~PmicDriver() { - I2cDriver::CloseSession(this->i2c_session); - I2cDriver::Finalize(); + sts::i2c::driver::CloseSession(this->i2c_session); + sts::i2c::driver::Finalize(); } private: Result GetPowerStatus(u8 *out); diff --git a/stratosphere/boot/source/boot_rtc_driver.hpp b/stratosphere/boot/source/boot_rtc_driver.hpp index 0fdafece6..3a8d3ccfa 100644 --- a/stratosphere/boot/source/boot_rtc_driver.hpp +++ b/stratosphere/boot/source/boot_rtc_driver.hpp @@ -18,20 +18,20 @@ #include #include -#include "i2c_driver/i2c_api.hpp" +#include "i2c/driver/i2c_api.hpp" class RtcDriver { private: - I2cSessionImpl i2c_session; + sts::i2c::driver::Session i2c_session; public: RtcDriver() { - I2cDriver::Initialize(); - I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc); + sts::i2c::driver::Initialize(); + sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc); } ~RtcDriver() { - I2cDriver::CloseSession(this->i2c_session); - I2cDriver::Finalize(); + sts::i2c::driver::CloseSession(this->i2c_session); + sts::i2c::driver::Finalize(); } private: Result ReadRtcRegister(u8 *out, u8 address); diff --git a/stratosphere/boot/source/i2c/driver/i2c_api.cpp b/stratosphere/boot/source/i2c/driver/i2c_api.cpp new file mode 100644 index 000000000..84e49e9d5 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/i2c_api.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_api.hpp" +#include "impl/i2c_resource_manager.hpp" + +namespace sts::i2c::driver { + + namespace { + + /* For convenience. */ + using CommandHandler = Result (*)(const u8 **cur_cmd, u8 **cur_dst, Session& session); + + /* Command handlers. */ + Result SendHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { + I2cTransactionOption option = static_cast( + (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) + ); + (*cur_cmd)++; + + size_t num_bytes = (**cur_cmd); + (*cur_cmd)++; + + R_TRY(Send(session, *cur_cmd, num_bytes, option)); + (*cur_cmd) += num_bytes; + + return ResultSuccess; + } + + Result ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { + I2cTransactionOption option = static_cast( + (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) + ); + (*cur_cmd)++; + + size_t num_bytes = (**cur_cmd); + (*cur_cmd)++; + + R_TRY(Receive(session, *cur_dst, num_bytes, option)); + (*cur_dst) += num_bytes; + + return ResultSuccess; + } + + Result SubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { + const SubCommand sub_cmd = static_cast((**cur_cmd) >> 2); + (*cur_cmd)++; + + switch (sub_cmd) { + case SubCommand::Sleep: + { + const size_t us = (**cur_cmd); + (*cur_cmd)++; + svcSleepThread(us * 1'000ul); + } + break; + default: + std::abort(); + } + return ResultSuccess; + } + + /* Command handler list. */ + constexpr CommandHandler g_cmd_handlers[static_cast(Command::Count)] = { + SendHandler, + ReceiveHandler, + SubCommandHandler, + }; + + inline impl::ResourceManager &GetResourceManager() { + return impl::ResourceManager::GetInstance(); + } + + inline void CheckInitialized() { + if (!GetResourceManager().IsInitialized()) { + std::abort(); + } + } + + } + + /* Initialization. */ + void Initialize() { + GetResourceManager().Initialize(); + } + + void Finalize() { + GetResourceManager().Finalize(); + } + + /* Session management. */ + void OpenSession(Session *out_session, I2cDevice device) { + CheckInitialized(); + if (!impl::IsDeviceSupported(device)) { + std::abort(); + } + + const auto bus = impl::GetDeviceBus(device); + const auto slave_address = impl::GetDeviceSlaveAddress(device); + const auto addressing_mode = impl::GetDeviceAddressingMode(device); + const auto speed_mode = impl::GetDeviceSpeedMode(device); + const auto max_retries = impl::GetDeviceMaxRetries(device); + const auto retry_wait_time = impl::GetDeviceRetryWaitTime(device); + GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time); + } + + void CloseSession(Session &session) { + CheckInitialized(); + GetResourceManager().CloseSession(session); + } + + /* Communication. */ + Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) { + CheckInitialized(); + if (src == nullptr || size == 0) { + std::abort(); + } + + std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); + return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send); + } + + Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) { + CheckInitialized(); + if (dst == nullptr || size == 0) { + std::abort(); + } + + std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); + return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive); + } + + Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) { + CheckInitialized(); + if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) { + std::abort(); + } + + u8 *cur_dst = static_cast(dst); + const u8 *cur_cmd = static_cast(cmd_list); + const u8 *cmd_list_end = cur_cmd + cmd_list_size; + + while (cur_cmd < cmd_list_end) { + Command cmd = static_cast((*cur_cmd) & 3); + if (cmd >= Command::Count) { + std::abort(); + } + + R_TRY(g_cmd_handlers[static_cast(cmd)](&cur_cmd, &cur_dst, session)); + } + + return ResultSuccess; + } + + /* Power management. */ + void SuspendBuses() { + GetResourceManager().SuspendBuses(); + } + + void ResumeBuses() { + GetResourceManager().ResumeBuses(); + } + + void SuspendPowerBus() { + GetResourceManager().SuspendPowerBus(); + } + + void ResumePowerBus() { + GetResourceManager().ResumePowerBus(); + } + +} diff --git a/stratosphere/boot/source/i2c/driver/i2c_api.hpp b/stratosphere/boot/source/i2c/driver/i2c_api.hpp new file mode 100644 index 000000000..134e00d4f --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/i2c_api.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "../i2c_types.hpp" +#include "../i2c_command_list.hpp" + +namespace sts::i2c::driver { + + struct Session { + size_t bus_idx; + size_t session_id; + }; + + /* Initialization. */ + void Initialize(); + void Finalize(); + + /* Session management. */ + void OpenSession(Session *out_session, I2cDevice device); + void CloseSession(Session &session); + + /* Communication. */ + Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option); + Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option); + Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size); + + /* Power management. */ + void SuspendBuses(); + void ResumeBuses(); + void SuspendPowerBus(); + void ResumePowerBus(); + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp new file mode 100644 index 000000000..02bf2ddcd --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_pcv.hpp" +#include "i2c_bus_accessor.hpp" + +namespace sts::i2c::driver::impl { + + void BusAccessor::Open(Bus bus, SpeedMode speed_mode) { + std::scoped_lock lk(this->open_mutex); + /* Open new session. */ + this->open_sessions++; + + /* Ensure we're good if this isn't our first session. */ + if (this->open_sessions > 1) { + if (this->speed_mode != speed_mode) { + std::abort(); + } + return; + } + + /* Set all members for chosen bus. */ + { + std::scoped_lock lk(this->register_mutex); + /* Set bus/registers. */ + this->SetBus(bus); + /* Set pcv module. */ + this->pcv_module = ConvertToPcvModule(bus); + /* Set speed mode. */ + this->speed_mode = speed_mode; + /* Setup interrupt event. */ + this->CreateInterruptEvent(bus); + } + } + + void BusAccessor::Close() { + std::scoped_lock lk(this->open_mutex); + /* Close current session. */ + this->open_sessions--; + if (this->open_sessions > 0) { + return; + } + + /* Close interrupt event. */ + eventClose(&this->interrupt_event); + + /* Close PCV. */ + pcv::Finalize(); + + this->suspended = false; + } + + void BusAccessor::Suspend() { + std::scoped_lock lk(this->open_mutex); + std::scoped_lock lk_reg(this->register_mutex); + + if (!this->suspended) { + this->suspended = true; + + if (this->pcv_module != PcvModule_I2C5) { + this->DisableClock(); + } + } + } + + void BusAccessor::Resume() { + if (this->suspended) { + this->DoInitialConfig(); + this->suspended = false; + } + } + + void BusAccessor::DoInitialConfig() { + std::scoped_lock lk(this->register_mutex); + + if (this->pcv_module != PcvModule_I2C5) { + pcv::Initialize(); + } + + this->ResetController(); + this->SetClock(this->speed_mode); + this->SetPacketMode(); + this->FlushFifos(); + } + + size_t BusAccessor::GetOpenSessions() const { + return this->open_sessions; + } + + bool BusAccessor::GetBusy() const { + /* Nintendo has a loop here that calls a member function to check if busy, retrying a few times. */ + /* This member function does "return false". */ + /* We will not bother with the loop. */ + return false; + } + + void BusAccessor::OnStartTransaction() const { + /* Nothing actually happens here. */ + } + + void BusAccessor::OnStopTransaction() const { + /* Nothing actually happens here. */ + } + + Result BusAccessor::StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address) { + /* Nothing actually happens here... */ + return ResultSuccess; + } + + Result BusAccessor::Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { + std::scoped_lock lk(this->register_mutex); + const u8 *cur_src = data; + size_t remaining = num_bytes; + + /* Set interrupt enable, clear interrupt status. */ + WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8E); + WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); + + ON_SCOPE_EXIT { this->ClearInterruptMask(); }; + + /* Send header. */ + this->WriteTransferHeader(TransferMode::Send, option, addressing_mode, slave_address, num_bytes); + + /* Send bytes. */ + while (true) { + const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0); + const size_t fifo_cnt = (fifo_status >> 4); + + for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + u32 val = 0; + for (size_t i = 0; i < cur_bytes; i++) { + val |= cur_src[i] << (8 * i); + } + WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val); + + cur_src += cur_bytes; + remaining -= cur_bytes; + } + + if (remaining == 0) { + break; + } + + eventClear(&this->interrupt_event); + if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { + this->HandleTransactionResult(ResultI2cBusBusy); + eventClear(&this->interrupt_event); + return ResultI2cTimedOut; + } + + R_TRY(this->GetAndHandleTransactionResult()); + } + + WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C); + + /* Wait for successful completion. */ + while (true) { + R_TRY(this->GetAndHandleTransactionResult()); + + /* Check PACKET_XFER_COMPLETE */ + const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); + if (interrupt_status & 0x80) { + R_TRY(this->GetAndHandleTransactionResult()); + break; + } + + eventClear(&this->interrupt_event); + if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { + this->HandleTransactionResult(ResultI2cBusBusy); + eventClear(&this->interrupt_event); + return ResultI2cTimedOut; + } + } + + return ResultSuccess; + } + + Result BusAccessor::Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { + std::scoped_lock lk(this->register_mutex); + u8 *cur_dst = out_data; + size_t remaining = num_bytes; + + /* Set interrupt enable, clear interrupt status. */ + WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8D); + WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); + + /* Send header. */ + this->WriteTransferHeader(TransferMode::Receive, option, addressing_mode, slave_address, num_bytes); + + /* Receive bytes. */ + while (remaining > 0) { + eventClear(&this->interrupt_event); + if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { + this->HandleTransactionResult(ResultI2cBusBusy); + this->ClearInterruptMask(); + eventClear(&this->interrupt_event); + return ResultI2cTimedOut; + } + + R_TRY(this->GetAndHandleTransactionResult()); + + const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0); + const size_t fifo_cnt = std::min((remaining + 3) >> 2, static_cast(fifo_status & 0xF)); + + for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { + const u32 val = ReadRegister(&this->i2c_registers->I2C_I2C_RX_FIFO_0); + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + for (size_t i = 0; i < cur_bytes; i++) { + cur_dst[i] = static_cast((val >> (8 * i)) & 0xFF); + } + + cur_dst += cur_bytes; + remaining -= cur_bytes; + } + } + + /* N doesn't do ClearInterruptMask. */ + return ResultSuccess; + } + + void BusAccessor::SetBus(Bus bus) { + this->bus = bus; + this->i2c_registers = GetRegisters(bus); + this->clkrst_registers.SetBus(bus); + } + + void BusAccessor::CreateInterruptEvent(Bus bus) { + static constexpr u64 s_interrupts[] = { + 0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F + }; + if (ConvertToIndex(bus) >= sizeof(s_interrupts) / sizeof(s_interrupts[0])) { + std::abort(); + } + + Handle evt_h; + if (R_FAILED(svcCreateInterruptEvent(&evt_h, s_interrupts[ConvertToIndex(bus)], 1))) { + std::abort(); + } + + eventLoadRemote(&this->interrupt_event, evt_h, false); + } + + void BusAccessor::SetClock(SpeedMode speed_mode) { + u32 t_high, t_low; + u32 clk_div, src_div; + u32 debounce; + + switch (speed_mode) { + case SpeedMode::Normal: + t_high = 2; + t_low = 4; + clk_div = 0x19; + src_div = 0x13; + debounce = 2; + break; + case SpeedMode::Fast: + t_high = 2; + t_low = 4; + clk_div = 0x19; + src_div = 0x04; + debounce = 2; + break; + case SpeedMode::FastPlus: + t_high = 2; + t_low = 4; + clk_div = 0x10; + src_div = 0x02; + debounce = 0; + break; + case SpeedMode::HighSpeed: + t_high = 3; + t_low = 8; + clk_div = 0x02; + src_div = 0x02; + debounce = 0; + break; + default: + std::abort(); + } + + if (speed_mode == SpeedMode::HighSpeed) { + WriteRegister(&this->i2c_registers->I2C_I2C_HS_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); + WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, clk_div); + } else { + WriteRegister(&this->i2c_registers->I2C_I2C_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); + WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, (clk_div << 16)); + } + + WriteRegister(&this->i2c_registers->I2C_I2C_CNFG_0, debounce); + ReadRegister(&this->i2c_registers->I2C_I2C_CNFG_0); + + if (this->pcv_module != PcvModule_I2C5) { + if (R_FAILED(pcv::SetReset(this->pcv_module, true))) { + std::abort(); + } + if (R_FAILED(pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)))) { + std::abort(); + } + if (R_FAILED(pcv::SetReset(this->pcv_module, false))) { + std::abort(); + } + } + } + + void BusAccessor::ResetController() const { + if (this->pcv_module != PcvModule_I2C5) { + if (R_FAILED(pcv::SetReset(this->pcv_module, true))) { + std::abort(); + } + if (R_FAILED(pcv::SetClockRate(this->pcv_module, 81'600'000))) { + std::abort(); + } + if (R_FAILED(pcv::SetReset(this->pcv_module, false))) { + std::abort(); + } + } + } + + void BusAccessor::ClearBus() const { + bool success = false; + for (size_t i = 0; i < 3 && !success; i++) { + success = true; + + this->ResetController(); + + WriteRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x90000); + SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x4); + SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x2); + + SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); + { + u64 start_tick = armGetSystemTick(); + while (ReadRegister(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) { + if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { + success = false; + break; + } + } + } + if (!success) { + continue; + } + + SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1); + { + u64 start_tick = armGetSystemTick(); + while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) { + if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { + success = false; + break; + } + } + } + if (!success) { + continue; + } + + { + u64 start_tick = armGetSystemTick(); + while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) { + if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { + success = false; + break; + } + } + } + if (!success) { + continue; + } + } + } + + void BusAccessor::DisableClock() { + if (R_FAILED(pcv::SetClockEnabled(this->pcv_module, false))) { + std::abort(); + } + } + + void BusAccessor::SetPacketMode() { + /* Set PACKET_MODE_EN, MSTR_CONFIG_LOAD */ + SetRegisterBits(&this->i2c_registers->I2C_I2C_CNFG_0, 0x400); + SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); + + /* Set TX_FIFO_TRIGGER, RX_FIFO_TRIGGER */ + WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC); + } + + Result BusAccessor::FlushFifos() { + WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFF); + + /* Wait for flush to finish, check every ms for 5 ms. */ + for (size_t i = 0; i < 5; i++) { + if (!(ReadRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3)) { + return ResultSuccess; + } + svcSleepThread(1'000'000ul); + } + + return ResultI2cBusBusy; + } + + Result BusAccessor::GetTransactionResult() const { + const u32 packet_status = ReadRegister(&this->i2c_registers->I2C_PACKET_TRANSFER_STATUS_0); + const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); + + /* Check for no ack. */ + if ((packet_status & 0xC) || (interrupt_status & 0x8)) { + return ResultI2cNoAck; + } + + /* Check for arb lost. */ + if ((packet_status & 0x2) || (interrupt_status & 0x4)) { + this->ClearBus(); + return ResultI2cBusBusy; + } + + return ResultSuccess; + } + + void BusAccessor::HandleTransactionResult(Result result) { + if (R_FAILED(result)) { + if (result == ResultI2cNoAck || result == ResultI2cBusBusy) { + this->ResetController(); + this->SetClock(this->speed_mode); + this->SetPacketMode(); + this->FlushFifos(); + } else { + std::abort(); + } + } + } + + Result BusAccessor::GetAndHandleTransactionResult() { + const Result transaction_res = this->GetTransactionResult(); + R_TRY_CLEANUP(transaction_res, { + this->HandleTransactionResult(transaction_res); + this->ClearInterruptMask(); + eventClear(&this->interrupt_event); + }); + return ResultSuccess; + } + + void BusAccessor::WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes) { + this->FlushFifos(); + + WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, 0x10); + WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, static_cast(num_bytes - 1) & 0xFFF); + + const u32 slave_addr_val = ((transfer_mode == TransferMode::Receive) & 1) | ((slave_address & 0x7F) << 1); + u32 hdr_val = 0; + hdr_val |= ((this->speed_mode == SpeedMode::HighSpeed) & 1) << 22; + hdr_val |= ((transfer_mode == TransferMode::Receive) & 1) << 19; + hdr_val |= ((addressing_mode != AddressingMode::SevenBit) & 1) << 18; + hdr_val |= (1 << 17); + hdr_val |= (((option & I2cTransactionOption_Stop) == 0) & 1) << 16; + hdr_val |= slave_addr_val; + + WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val); + } + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp new file mode 100644 index 000000000..23c4990d4 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "i2c_driver_types.hpp" +#include "i2c_registers.hpp" + +namespace sts::i2c::driver::impl { + + class BusAccessor { + private: + enum class TransferMode { + Send = 0, + Receive = 1, + }; + static constexpr u64 InterruptTimeout = 100'000'000ul; + private: + Event interrupt_event; + HosMutex open_mutex; + HosMutex register_mutex; + Registers *i2c_registers = nullptr; + ClkRstRegisters clkrst_registers; + SpeedMode speed_mode = SpeedMode::Fast; + size_t open_sessions = 0; + Bus bus = Bus::I2C1; + PcvModule pcv_module = PcvModule_I2C1; + bool suspended = false; + public: + BusAccessor() { /* ... */ } + private: + inline void ClearInterruptMask() const { + WriteRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0); + ReadRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0); + } + + void SetBus(Bus bus); + void CreateInterruptEvent(Bus bus); + void SetClock(SpeedMode speed_mode); + + void ResetController() const; + void ClearBus() const; + void DisableClock(); + void SetPacketMode(); + Result FlushFifos(); + + Result GetTransactionResult() const; + void HandleTransactionResult(Result result); + Result GetAndHandleTransactionResult(); + + void WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes); + public: + void Open(Bus bus, SpeedMode speed_mode); + void Close(); + void Suspend(); + void Resume(); + void DoInitialConfig(); + + size_t GetOpenSessions() const; + bool GetBusy() const; + + void OnStartTransaction() const; + Result StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address); + Result Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); + Result Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); + void OnStopTransaction() const; + }; + + +} \ No newline at end of file diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp new file mode 100644 index 000000000..543e63afd --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_driver_types.hpp" + +namespace sts::i2c::driver::impl { + + namespace { + + struct DeviceConfig { + I2cDevice device; + Bus bus; + u32 slave_address; + AddressingMode addressing_mode; + SpeedMode speed_mode; + u32 max_retries; + u64 retry_wait_time; + }; + + constexpr DeviceConfig g_device_configs[I2cDevice_Count] = { + {I2cDevice_DebugPad, Bus::I2C1, 0x52, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_TouchPanel, Bus::I2C3, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, + {I2cDevice_Tmp451, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_Nct72, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_Alc5639, Bus::I2C1, 0x1c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_Max77620Rtc, Bus::I2C5, 0x68, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Max77620Pmic, Bus::I2C5, 0x3c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Max77621Cpu, Bus::I2C5, 0x1b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Max77621Gpu, Bus::I2C5, 0x1c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Bq24193, Bus::I2C1, 0x6b, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, + {I2cDevice_Max17050, Bus::I2C1, 0x36, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, + {I2cDevice_Bm92t30mwv, Bus::I2C1, 0x18, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, + {I2cDevice_Ina226Vdd15v0Hb, Bus::I2C2, 0x40, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysCpuDs, Bus::I2C2, 0x41, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysGpuDs, Bus::I2C2, 0x44, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysDdrDs, Bus::I2C2, 0x45, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysAp, Bus::I2C2, 0x46, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysBlDs, Bus::I2C2, 0x47, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Bh1730, Bus::I2C2, 0x29, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226VsysCore, Bus::I2C2, 0x48, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226Soc1V8, Bus::I2C2, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226Lpddr1V8, Bus::I2C2, 0x4a, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226Reg1V32, Bus::I2C2, 0x4b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_Ina226Vdd3V3Sys, Bus::I2C2, 0x4d, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + {I2cDevice_HdmiDdc, Bus::I2C4, 0x50, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_HdmiScdc, Bus::I2C4, 0x54, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_HdmiHdcp, Bus::I2C4, 0x3a, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, + {I2cDevice_Fan53528, Bus::I2C5, 0xa4, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, + {I2cDevice_Max77812_3, Bus::I2C5, 0x31, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, + {I2cDevice_Max77812_2, Bus::I2C5, 0x33, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, + {I2cDevice_Ina226VddDdr0V6, Bus::I2C2, 0x4e, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, + }; + + constexpr size_t NumDeviceConfigs = sizeof(g_device_configs) / sizeof(g_device_configs[0]); + + constexpr size_t DeviceInvalidIndex = static_cast(-1); + + size_t GetDeviceIndex(I2cDevice dev) { + for (size_t i = 0; i < NumDeviceConfigs; i++) { + if (g_device_configs[i].device == dev) { + return i; + } + } + return DeviceInvalidIndex; + } + + } + + bool IsDeviceSupported(I2cDevice dev) { + return GetDeviceIndex(dev) != DeviceInvalidIndex; + } + + Bus GetDeviceBus(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].bus; + } + + u32 GetDeviceSlaveAddress(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].slave_address; + } + + AddressingMode GetDeviceAddressingMode(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].addressing_mode; + } + + SpeedMode GetDeviceSpeedMode(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].speed_mode; + } + + u32 GetDeviceMaxRetries(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].max_retries; + } + + u64 GetDeviceRetryWaitTime(I2cDevice dev) { + const size_t dev_idx = GetDeviceIndex(dev); + if (dev_idx == DeviceInvalidIndex) { std::abort(); } + return g_device_configs[dev_idx].retry_wait_time; + } + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp new file mode 100644 index 000000000..5fb2ec0e1 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "../../i2c_types.hpp" + +namespace sts::i2c::driver::impl { + + enum class Command { + Send = 0, + Receive = 1, + }; + + enum class Bus { + I2C1 = 0, + I2C2 = 1, + I2C3 = 2, + I2C4 = 3, + I2C5 = 4, + I2C6 = 5, + Count, + }; + + /* Bus helpers. */ + constexpr inline size_t ConvertToIndex(Bus bus) { + return static_cast(bus); + } + + constexpr inline Bus ConvertFromIndex(size_t idx) { + if (idx >= static_cast(Bus::Count)) { + std::abort(); + } + return static_cast(idx); + } + + constexpr inline PcvModule ConvertToPcvModule(Bus bus) { + switch (bus) { + case Bus::I2C1: + return PcvModule_I2C1; + case Bus::I2C2: + return PcvModule_I2C2; + case Bus::I2C3: + return PcvModule_I2C3; + case Bus::I2C4: + return PcvModule_I2C4; + case Bus::I2C5: + return PcvModule_I2C5; + case Bus::I2C6: + return PcvModule_I2C6; + default: + std::abort(); + } + } + + constexpr inline Bus ConvertFromPcvModule(PcvModule module) { + switch (module) { + case PcvModule_I2C1: + return Bus::I2C1; + case PcvModule_I2C2: + return Bus::I2C2; + case PcvModule_I2C3: + return Bus::I2C3; + case PcvModule_I2C4: + return Bus::I2C4; + case PcvModule_I2C5: + return Bus::I2C5; + case PcvModule_I2C6: + return Bus::I2C6; + default: + std::abort(); + } + } + + /* Global type functions. */ + bool IsDeviceSupported(I2cDevice dev); + Bus GetDeviceBus(I2cDevice dev); + u32 GetDeviceSlaveAddress(I2cDevice dev); + AddressingMode GetDeviceAddressingMode(I2cDevice dev); + SpeedMode GetDeviceSpeedMode(I2cDevice dev); + u32 GetDeviceMaxRetries(I2cDevice dev); + u64 GetDeviceRetryWaitTime(I2cDevice dev); + +} diff --git a/stratosphere/boot/source/i2c_driver/boot_pcv.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp similarity index 56% rename from stratosphere/boot/source/i2c_driver/boot_pcv.hpp rename to stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp index 9ccb244eb..3658784e2 100644 --- a/stratosphere/boot/source/i2c_driver/boot_pcv.hpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp @@ -18,16 +18,16 @@ #include #include -/* pcv isn't alive at the time boot runs, but nn::i2c::driver needs nn::pcv. */ -/* These are the overrides N puts in boot. */ +/* This forward declares the functionality from pcv that i2c::driver uses. */ +/* This allows for overriding at compile-time (e.g., for boot sysmodule). */ +namespace sts::pcv { -class Pcv { - public: - static void Initialize(); - static void Finalize(); - static Result SetClockRate(PcvModule module, u32 hz); - static Result SetClockEnabled(PcvModule module, bool enabled); - static Result SetVoltageEnabled(u32 domain, bool enabled); - static Result SetVoltageValue(u32 domain, u32 voltage); - static Result SetReset(PcvModule module, bool reset); -}; \ No newline at end of file + void Initialize(); + void Finalize(); + Result SetClockRate(PcvModule module, u32 hz); + Result SetClockEnabled(PcvModule module, bool enabled); + Result SetVoltageEnabled(u32 domain, bool enabled); + Result SetVoltageValue(u32 domain, u32 voltage); + Result SetReset(PcvModule module, bool reset); + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp new file mode 100644 index 000000000..f10a7fec1 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "i2c_driver_types.hpp" + +namespace sts::i2c::driver::impl { + + struct Registers { + volatile u32 I2C_I2C_CNFG_0; + volatile u32 I2C_I2C_CMD_ADDR0_0; + volatile u32 I2C_I2C_CMD_ADDR1_0; + volatile u32 I2C_I2C_CMD_DATA1_0; + volatile u32 I2C_I2C_CMD_DATA2_0; + volatile u32 _0x14; + volatile u32 _0x18; + volatile u32 I2C_I2C_STATUS_0; + volatile u32 I2C_I2C_SL_CNFG_0; + volatile u32 I2C_I2C_SL_RCVD_0; + volatile u32 I2C_I2C_SL_STATUS_0; + volatile u32 I2C_I2C_SL_ADDR1_0; + volatile u32 I2C_I2C_SL_ADDR2_0; + volatile u32 I2C_I2C_TLOW_SEXT_0; + volatile u32 _0x38; + volatile u32 I2C_I2C_SL_DELAY_COUNT_0; + volatile u32 I2C_I2C_SL_INT_MASK_0; + volatile u32 I2C_I2C_SL_INT_SOURCE_0; + volatile u32 I2C_I2C_SL_INT_SET_0; + volatile u32 _0x4C; + volatile u32 I2C_I2C_TX_PACKET_FIFO_0; + volatile u32 I2C_I2C_RX_FIFO_0; + volatile u32 I2C_PACKET_TRANSFER_STATUS_0; + volatile u32 I2C_FIFO_CONTROL_0; + volatile u32 I2C_FIFO_STATUS_0; + volatile u32 I2C_INTERRUPT_MASK_REGISTER_0; + volatile u32 I2C_INTERRUPT_STATUS_REGISTER_0; + volatile u32 I2C_I2C_CLK_DIVISOR_REGISTER_0; + volatile u32 I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; + volatile u32 I2C_I2C_INTERRUPT_SET_REGISTER_0; + volatile u32 I2C_I2C_SLV_TX_PACKET_FIFO_0; + volatile u32 I2C_I2C_SLV_RX_FIFO_0; + volatile u32 I2C_I2C_SLV_PACKET_STATUS_0; + volatile u32 I2C_I2C_BUS_CLEAR_CONFIG_0; + volatile u32 I2C_I2C_BUS_CLEAR_STATUS_0; + volatile u32 I2C_I2C_CONFIG_LOAD_0; + volatile u32 _0x90; + volatile u32 I2C_I2C_INTERFACE_TIMING_0_0; + volatile u32 I2C_I2C_INTERFACE_TIMING_1_0; + volatile u32 I2C_I2C_HS_INTERFACE_TIMING_0_0; + volatile u32 I2C_I2C_HS_INTERFACE_TIMING_1_0; + }; + + struct ClkRstRegisters { + public: + volatile u32 *clk_src_reg; + volatile u32 *clk_en_reg; + volatile u32 *rst_reg; + u32 mask; + public: + void SetBus(Bus bus) { + static constexpr uintptr_t s_clk_src_offsets[ConvertToIndex(Bus::Count)] = { + 0x124, 0x198, 0x1b8, 0x3c4, 0x128, 0x65c + }; + static constexpr uintptr_t s_clk_en_offsets[ConvertToIndex(Bus::Count)] = { + 0x010, 0x014, 0x018, 0x360, 0x014, 0x280 + }; + static constexpr uintptr_t s_rst_offsets[ConvertToIndex(Bus::Count)] = { + 0x004, 0x008, 0x00c, 0x358, 0x008, 0x28c + }; + static constexpr size_t s_bit_shifts[ConvertToIndex(Bus::Count)] = { + 12, 22, 3, 7, 15, 6 + }; + + const uintptr_t registers = GetIoMapping(0x60006000ul, 0x1000); + const size_t idx = ConvertToIndex(bus); + this->clk_src_reg = reinterpret_cast(registers + s_clk_src_offsets[idx]); + this->clk_en_reg = reinterpret_cast(registers + s_clk_en_offsets[idx]); + this->rst_reg = reinterpret_cast(registers + s_rst_offsets[idx]); + this->mask = (1u << s_bit_shifts[idx]); + } + }; + + inline Registers *GetRegisters(Bus bus) { + static constexpr uintptr_t s_offsets[ConvertToIndex(Bus::Count)] = { + 0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100 + }; + + const uintptr_t registers = GetIoMapping(0x7000c000ul, 0x2000) + s_offsets[ConvertToIndex(bus)]; + return reinterpret_cast(registers); + } + + inline void WriteRegister(volatile u32 *reg, u32 val) { + *reg = val; + } + + inline u32 ReadRegister(volatile u32 *reg) { + u32 val = *reg; + return val; + } + + inline void SetRegisterBits(volatile u32 *reg, u32 mask) { + *reg |= mask; + } + + inline void ClearRegisterBits(volatile u32 *reg, u32 mask) { + *reg &= mask; + } + + inline void ReadWriteRegisterBits(volatile u32 *reg, u32 val, u32 mask) { + *reg = (*reg & (~mask)) | (val & mask); + } + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp new file mode 100644 index 000000000..e7d01f34d --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_pcv.hpp" +#include "i2c_resource_manager.hpp" + +namespace sts::i2c::driver::impl { + + void ResourceManager::Initialize() { + std::scoped_lock lk(this->initialize_mutex); + this->ref_cnt++; + } + + void ResourceManager::Finalize() { + std::scoped_lock lk(this->initialize_mutex); + if (this->ref_cnt == 0) { + std::abort(); + } + this->ref_cnt--; + if (this->ref_cnt > 0) { + return; + } + + { + std::scoped_lock sess_lk(this->session_open_mutex); + for (size_t i = 0; i < MaxDriverSessions; i++) { + this->sessions[i].Close(); + } + } + } + + size_t ResourceManager::GetFreeSessionId() const { + for (size_t i = 0; i < MaxDriverSessions; i++) { + if (!this->sessions[i].IsOpen()) { + return i; + } + } + + return InvalidSessionId; + } + + void ResourceManager::OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time) { + bool need_enable_ldo6 = false; + size_t session_id = InvalidSessionId; + /* Get, open session. */ + { + std::scoped_lock lk(this->session_open_mutex); + if (out_session == nullptr || bus >= Bus::Count) { + std::abort(); + } + + session_id = GetFreeSessionId(); + if (session_id == InvalidSessionId) { + std::abort(); + } + + + if ((bus == Bus::I2C2 || bus == Bus::I2C3) && (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) { + need_enable_ldo6 = true; + } + + out_session->session_id = session_id; + out_session->bus_idx = ConvertToIndex(bus); + this->sessions[session_id].Open(bus, slave_address, addressing_mode, speed_mode, &this->bus_accessors[ConvertToIndex(bus)], max_retries, retry_wait_time); + } + + this->sessions[session_id].Start(); + if (need_enable_ldo6) { + pcv::Initialize(); + if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) { + std::abort(); + } + if (R_FAILED(pcv::SetVoltageEnabled(10, true))) { + std::abort(); + } + pcv::Finalize(); + svcSleepThread(560'000ul); + } + } + + void ResourceManager::CloseSession(const driver::Session &session) { + bool need_disable_ldo6 = false; + /* Get, open session. */ + { + std::scoped_lock lk(this->session_open_mutex); + if (!this->sessions[session.session_id].IsOpen()) { + std::abort(); + } + + this->sessions[session.session_id].Close(); + + if ((ConvertFromIndex(session.bus_idx) == Bus::I2C2 || ConvertFromIndex(session.bus_idx) == Bus::I2C3) && + (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) { + need_disable_ldo6 = true; + } + } + + if (need_disable_ldo6) { + pcv::Initialize(); + if (R_FAILED(pcv::SetVoltageEnabled(10, false))) { + std::abort(); + } + pcv::Finalize(); + } + + } + + void ResourceManager::SuspendBuses() { + if (this->ref_cnt == 0) { + std::abort(); + } + + if (!this->suspended) { + { + std::scoped_lock lk(this->session_open_mutex); + this->suspended = true; + for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) { + if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { + this->bus_accessors[i].Suspend(); + } + } + } + pcv::Initialize(); + if (R_FAILED(pcv::SetVoltageEnabled(10, false))) { + std::abort(); + } + pcv::Finalize(); + } + } + + void ResourceManager::ResumeBuses() { + if (this->ref_cnt == 0) { + std::abort(); + } + + if (this->suspended) { + if (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() > 0 || this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() > 0) { + pcv::Initialize(); + if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) { + std::abort(); + } + if (R_FAILED(pcv::SetVoltageEnabled(10, true))) { + std::abort(); + } + pcv::Finalize(); + svcSleepThread(1'560'000ul); + } + { + std::scoped_lock lk(this->session_open_mutex); + for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) { + if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { + this->bus_accessors[i].Resume(); + } + } + } + this->suspended = false; + } + } + + void ResourceManager::SuspendPowerBus() { + if (this->ref_cnt == 0) { + std::abort(); + } + std::scoped_lock lk(this->session_open_mutex); + + if (!this->power_bus_suspended) { + this->power_bus_suspended = true; + if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { + this->bus_accessors[PowerBusId].Suspend(); + } + } + } + + void ResourceManager::ResumePowerBus() { + if (this->ref_cnt == 0) { + std::abort(); + } + std::scoped_lock lk(this->session_open_mutex); + + if (this->power_bus_suspended) { + if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { + this->bus_accessors[PowerBusId].Resume(); + } + this->power_bus_suspended = false; + } + } + +} + diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp new file mode 100644 index 000000000..c0582491b --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "../i2c_api.hpp" +#include "i2c_driver_types.hpp" +#include "i2c_bus_accessor.hpp" +#include "i2c_session.hpp" + +namespace sts::i2c::driver::impl { + + class ResourceManager { + public: + static constexpr size_t MaxDriverSessions = 40; + static constexpr size_t PowerBusId = ConvertToIndex(Bus::I2C5); + static constexpr size_t InvalidSessionId = static_cast(-1); + private: + HosMutex initialize_mutex; + HosMutex session_open_mutex; + size_t ref_cnt = 0; + bool suspended = false; + bool power_bus_suspended = false; + Session sessions[MaxDriverSessions]; + BusAccessor bus_accessors[ConvertToIndex(Bus::Count)]; + HosMutex transaction_mutexes[ConvertToIndex(Bus::Count)]; + public: + ResourceManager() { + /* ... */ + } + private: + size_t GetFreeSessionId() const; + public: + /* N uses a singleton here, we'll oblige. */ + static ResourceManager &GetInstance() { + static ResourceManager s_instance; + return s_instance; + } + + bool IsInitialized() const { + return this->ref_cnt > 0; + } + + Session& GetSession(size_t id) { + return this->sessions[id]; + } + + HosMutex& GetTransactionMutex(Bus bus) { + return this->transaction_mutexes[ConvertToIndex(bus)]; + } + + void Initialize(); + void Finalize(); + + void OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time); + void CloseSession(const driver::Session &session); + void SuspendBuses(); + void ResumeBuses(); + void SuspendPowerBus(); + void ResumePowerBus(); + }; + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp new file mode 100644 index 000000000..f54231dc7 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_session.hpp" + +namespace sts::i2c::driver::impl { + + void Session::Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time) { + std::scoped_lock lk(this->bus_accessor_mutex); + if (!this->open) { + this->bus_accessor = bus_accessor; + this->bus = bus; + this->slave_address = slave_address; + this->addressing_mode = addr_mode; + this->max_retries = max_retries; + this->retry_wait_time = retry_wait_time; + this->bus_accessor->Open(this->bus, speed_mode); + this->open = true; + } + } + + void Session::Start() { + std::scoped_lock lk(this->bus_accessor_mutex); + if (this->open) { + if (this->bus_accessor->GetOpenSessions() == 1) { + this->bus_accessor->DoInitialConfig(); + } + } + } + + void Session::Close() { + std::scoped_lock lk(this->bus_accessor_mutex); + if (this->open) { + this->bus_accessor->Close(); + this->bus_accessor = nullptr; + this->open = false; + } + } + + bool Session::IsOpen() const { + return this->open; + } + + Result Session::DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) { + std::scoped_lock lk(this->bus_accessor_mutex); + + if (this->bus_accessor->GetBusy()) { + return ResultI2cBusBusy; + } + + this->bus_accessor->OnStartTransaction(); + ON_SCOPE_EXIT { this->bus_accessor->OnStopTransaction(); }; + + R_TRY(this->bus_accessor->StartTransaction(command, this->addressing_mode, this->slave_address)); + + switch (command) { + case Command::Send: + R_TRY(this->bus_accessor->Send(reinterpret_cast(src), num_bytes, option, this->addressing_mode, this->slave_address)); + break; + case Command::Receive: + R_TRY(this->bus_accessor->Receive(reinterpret_cast(dst), num_bytes, option, this->addressing_mode, this->slave_address)); + break; + default: + std::abort(); + } + + return ResultSuccess; + } + + Result Session::DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) { + size_t i = 0; + while (true) { + R_TRY_CATCH(this->DoTransaction(dst, src, num_bytes, option, command)) { + R_CATCH(ResultI2cTimedOut) { + i++; + if (i <= this->max_retries) { + svcSleepThread(this->retry_wait_time); + continue; + } + return ResultI2cBusBusy; + } + } R_END_TRY_CATCH; + return ResultSuccess; + } + } + +} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp new file mode 100644 index 000000000..b9e162523 --- /dev/null +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "i2c_driver_types.hpp" +#include "i2c_bus_accessor.hpp" + +namespace sts::i2c::driver::impl { + + class Session { + private: + HosMutex bus_accessor_mutex; + BusAccessor *bus_accessor = nullptr; + Bus bus = Bus::I2C1; + u32 slave_address = 0; + AddressingMode addressing_mode = AddressingMode::SevenBit; + u32 max_retries = 0; + u64 retry_wait_time = 0; + bool open = false; + public: + Session() { /* ... */ } + public: + void Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time); + void Start(); + void Close(); + + bool IsOpen() const; + + Result DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command); + Result DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command); + }; + +} + + diff --git a/stratosphere/boot/source/i2c/i2c_command_list.cpp b/stratosphere/boot/source/i2c/i2c_command_list.cpp new file mode 100644 index 000000000..838246e39 --- /dev/null +++ b/stratosphere/boot/source/i2c/i2c_command_list.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c_types.hpp" +#include "i2c_command_list.hpp" + +namespace sts::i2c { + + namespace { + + /* Useful definitions. */ + constexpr size_t SendCommandSize = 2; + constexpr size_t ReceiveCommandSize = 2; + constexpr size_t SleepCommandSize = 2; + + } + + Result CommandListFormatter::CanEnqueue(size_t size) const { + if (this->cmd_list_size - this->cur_index < size) { + return ResultI2cFullCommandList; + } + return ResultSuccess; + } + + Result CommandListFormatter::EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size) { + R_TRY(this->CanEnqueue(SendCommandSize + size)); + + this->cmd_list[this->cur_index] = static_cast(Command::Send); + this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; + this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; + this->cur_index++; + + this->cmd_list[this->cur_index++] = size; + + const u8 *src_u8 = reinterpret_cast(src); + for (size_t i = 0; i < size; i++) { + this->cmd_list[this->cur_index++] = src_u8[i]; + } + return ResultSuccess; + } + + Result CommandListFormatter::EnqueueReceiveCommand(I2cTransactionOption option, size_t size) { + R_TRY(this->CanEnqueue(ReceiveCommandSize)); + + this->cmd_list[this->cur_index] = static_cast(Command::Receive); + this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; + this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; + this->cur_index++; + + this->cmd_list[this->cur_index++] = size; + return ResultSuccess; + } + + Result CommandListFormatter::EnqueueSleepCommand(size_t us) { + R_TRY(this->CanEnqueue(SleepCommandSize)); + + this->cmd_list[this->cur_index] = static_cast(Command::SubCommand); + this->cmd_list[this->cur_index] |= static_cast(SubCommand::Sleep) << 2; + this->cur_index++; + + this->cmd_list[this->cur_index++] = us; + return ResultSuccess; + } + +} + + diff --git a/stratosphere/boot/source/i2c/i2c_command_list.hpp b/stratosphere/boot/source/i2c/i2c_command_list.hpp new file mode 100644 index 000000000..4ca94c881 --- /dev/null +++ b/stratosphere/boot/source/i2c/i2c_command_list.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "i2c_types.hpp" + +namespace sts::i2c { + + enum class Command { + Send = 0, + Receive = 1, + SubCommand = 2, + Count, + }; + + enum class SubCommand { + Sleep = 0, + Count, + }; + + class CommandListFormatter { + public: + static constexpr size_t MaxCommandListSize = 0x100; + private: + u8 *cmd_list = nullptr; + size_t cmd_list_size = 0; + size_t cur_index = 0; + public: + CommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast(cmd_list)), cmd_list_size(cmd_list_size) { + if (cmd_list_size > MaxCommandListSize) { + std::abort(); + } + } + ~CommandListFormatter() { + this->cmd_list = nullptr; + } + + private: + Result CanEnqueue(size_t size) const; + public: + size_t GetCurrentSize() const { + return this->cur_index; + } + + Result EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size); + Result EnqueueReceiveCommand(I2cTransactionOption option, size_t size); + Result EnqueueSleepCommand(size_t us); + }; + +} diff --git a/stratosphere/boot/source/i2c/i2c_types.hpp b/stratosphere/boot/source/i2c/i2c_types.hpp new file mode 100644 index 000000000..e529d7f6d --- /dev/null +++ b/stratosphere/boot/source/i2c/i2c_types.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace sts::i2c { + + enum class AddressingMode { + SevenBit = 0, + }; + + enum class SpeedMode { + Normal = 100000, + Fast = 400000, + FastPlus = 1000000, + HighSpeed = 3400000, + }; + +} diff --git a/stratosphere/boot/source/i2c_driver/boot_pcv.cpp b/stratosphere/boot/source/i2c_driver/boot_pcv.cpp deleted file mode 100644 index 6ef431a42..000000000 --- a/stratosphere/boot/source/i2c_driver/boot_pcv.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_types.hpp" -#include "i2c_registers.hpp" -#include "boot_pcv.hpp" - -static I2cBus GetI2cBus(PcvModule module) { - switch (module) { - case PcvModule_I2C1: - return I2cBus_I2c1; - case PcvModule_I2C2: - return I2cBus_I2c2; - case PcvModule_I2C3: - return I2cBus_I2c3; - case PcvModule_I2C4: - return I2cBus_I2c4; - case PcvModule_I2C5: - return I2cBus_I2c5; - case PcvModule_I2C6: - return I2cBus_I2c6; - default: - std::abort(); - } -} - -void Pcv::Initialize() { - /* Don't do anything. */ -} - -void Pcv::Finalize() { - /* Don't do anything. */ -} - -Result Pcv::SetClockRate(PcvModule module, u32 hz) { - /* Get clock/reset registers. */ - ClkRstRegisters regs; - regs.SetBus(GetI2cBus(module)); - /* Set clock enabled/source. */ - SetRegisterBits(regs.clk_en_reg, regs.mask); - ReadWriteRegisterBits(regs.clk_src_reg, 0x4, 0xFF); - svcSleepThread(1000ul); - ReadWriteRegisterBits(regs.clk_src_reg, 0, 0xE0000000); - svcSleepThread(2000ul); - - return ResultSuccess; -} - -Result Pcv::SetClockEnabled(PcvModule module, bool enabled) { - return ResultSuccess; -} - -Result Pcv::SetVoltageEnabled(u32 domain, bool enabled) { - return ResultSuccess; -} - -Result Pcv::SetVoltageValue(u32 domain, u32 voltage) { - return ResultSuccess; -} - -Result Pcv::SetReset(PcvModule module, bool reset) { - /* Get clock/reset registers. */ - ClkRstRegisters regs; - regs.SetBus(GetI2cBus(module)); - - /* Set/clear reset. */ - if (reset) { - SetRegisterBits(regs.rst_reg, regs.mask); - } else { - ClearRegisterBits(regs.rst_reg, ~regs.mask); - } - - return ResultSuccess; -} \ No newline at end of file diff --git a/stratosphere/boot/source/i2c_driver/i2c_api.cpp b/stratosphere/boot/source/i2c_driver/i2c_api.cpp deleted file mode 100644 index 2ff55f694..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_api.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_api.hpp" -#include "i2c_resource_manager.hpp" - -typedef Result (*CommandHandler)(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session); - -static Result I2cSendHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) { - I2cTransactionOption option = static_cast( - (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) - ); - (*cur_cmd)++; - - size_t num_bytes = (**cur_cmd); - (*cur_cmd)++; - - R_TRY(I2cDriver::Send(session, *cur_cmd, num_bytes, option)); - (*cur_cmd) += num_bytes; - - return ResultSuccess; -} - -static Result I2cReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) { - I2cTransactionOption option = static_cast( - (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) - ); - (*cur_cmd)++; - - size_t num_bytes = (**cur_cmd); - (*cur_cmd)++; - - R_TRY(I2cDriver::Receive(session, *cur_dst, num_bytes, option)); - (*cur_dst) += num_bytes; - - return ResultSuccess; -} - -static Result I2cSubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) { - const I2cSubCommand sub_cmd = static_cast((**cur_cmd) >> 2); - (*cur_cmd)++; - - switch (sub_cmd) { - case I2cSubCommand_Sleep: - { - const size_t us = (**cur_cmd); - (*cur_cmd)++; - svcSleepThread(us * 1'000ul); - } - break; - default: - std::abort(); - } - return ResultSuccess; -} - -static constexpr CommandHandler g_cmd_handlers[I2cCommand_Count] = { - I2cSendHandler, - I2cReceiveHandler, - I2cSubCommandHandler, -}; - -static inline I2cResourceManager &GetResourceManager() { - return I2cResourceManager::GetInstance(); -} - -static inline void CheckInitialized() { - if (!GetResourceManager().IsInitialized()) { - std::abort(); - } -} - -void I2cDriver::Initialize() { - GetResourceManager().Initialize(); -} - -void I2cDriver::Finalize() { - GetResourceManager().Finalize(); -} - -void I2cDriver::OpenSession(I2cSessionImpl *out_session, I2cDevice device) { - CheckInitialized(); - if (!IsI2cDeviceSupported(device)) { - std::abort(); - } - - const I2cBus bus = GetI2cDeviceBus(device); - const u32 slave_address = GetI2cDeviceSlaveAddress(device); - const AddressingMode addressing_mode = GetI2cDeviceAddressingMode(device); - const SpeedMode speed_mode = GetI2cDeviceSpeedMode(device); - const u32 max_retries = GetI2cDeviceMaxRetries(device); - const u64 retry_wait_time = GetI2cDeviceRetryWaitTime(device); - GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time); -} - -void I2cDriver::CloseSession(I2cSessionImpl &session) { - CheckInitialized(); - GetResourceManager().CloseSession(session); -} - -Result I2cDriver::Send(I2cSessionImpl &session, const void *src, size_t size, I2cTransactionOption option) { - CheckInitialized(); - if (src == nullptr || size == 0) { - std::abort(); - } - - std::scoped_lock lk(GetResourceManager().GetTransactionMutex(session.bus)); - return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, DriverCommand_Send); -} - -Result I2cDriver::Receive(I2cSessionImpl &session, void *dst, size_t size, I2cTransactionOption option) { - CheckInitialized(); - if (dst == nullptr || size == 0) { - std::abort(); - } - - std::scoped_lock lk(GetResourceManager().GetTransactionMutex(session.bus)); - return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, DriverCommand_Receive); -} - -Result I2cDriver::ExecuteCommandList(I2cSessionImpl &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) { - CheckInitialized(); - if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) { - std::abort(); - } - - u8 *cur_dst = static_cast(dst); - const u8 *cur_cmd = static_cast(cmd_list); - const u8 *cmd_list_end = cur_cmd + cmd_list_size; - - while (cur_cmd < cmd_list_end) { - I2cCommand cmd = static_cast((*cur_cmd) & 3); - if (cmd >= I2cCommand_Count) { - std::abort(); - } - - R_TRY(g_cmd_handlers[cmd](&cur_cmd, &cur_dst, session)); - } - - return ResultSuccess; -} - -void I2cDriver::SuspendBuses() { - GetResourceManager().SuspendBuses(); -} - -void I2cDriver::ResumeBuses() { - GetResourceManager().ResumeBuses(); -} - -void I2cDriver::SuspendPowerBus() { - GetResourceManager().SuspendPowerBus(); -} - -void I2cDriver::ResumePowerBus() { - GetResourceManager().ResumePowerBus(); -} \ No newline at end of file diff --git a/stratosphere/boot/source/i2c_driver/i2c_api.hpp b/stratosphere/boot/source/i2c_driver/i2c_api.hpp deleted file mode 100644 index 1db002ff2..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_api.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" -#include "i2c_command_list.hpp" - -class I2cDriver { - public: - static void Initialize(); - static void Finalize(); - static void OpenSession(I2cSessionImpl *out_session, I2cDevice device); - static void CloseSession(I2cSessionImpl &session); - static Result Send(I2cSessionImpl &session, const void *src, size_t size, I2cTransactionOption option); - static Result Receive(I2cSessionImpl &session, void *dst, size_t size, I2cTransactionOption option); - static Result ExecuteCommandList(I2cSessionImpl &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size); - - static void SuspendBuses(); - static void ResumeBuses(); - static void SuspendPowerBus(); - static void ResumePowerBus(); -}; \ No newline at end of file diff --git a/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.cpp deleted file mode 100644 index 508b3e779..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_bus_accessor.hpp" -#include "boot_pcv.hpp" - -void I2cBusAccessor::Open(I2cBus bus, SpeedMode speed_mode) { - std::scoped_lock lk(this->open_mutex); - /* Open new session. */ - this->open_sessions++; - - /* Ensure we're good if this isn't our first session. */ - if (this->open_sessions > 1) { - if (this->speed_mode != speed_mode) { - std::abort(); - } - return; - } - - /* Set all members for chosen bus. */ - { - std::scoped_lock lk(this->register_mutex); - /* Set bus/registers. */ - this->SetBus(bus); - /* Set pcv module. */ - switch (bus) { - case I2cBus_I2c1: - this->pcv_module = PcvModule_I2C1; - break; - case I2cBus_I2c2: - this->pcv_module = PcvModule_I2C2; - break; - case I2cBus_I2c3: - this->pcv_module = PcvModule_I2C3; - break; - case I2cBus_I2c4: - this->pcv_module = PcvModule_I2C4; - break; - case I2cBus_I2c5: - this->pcv_module = PcvModule_I2C5; - break; - case I2cBus_I2c6: - this->pcv_module = PcvModule_I2C6; - break; - default: - std::abort(); - } - /* Set speed mode. */ - this->speed_mode = speed_mode; - /* Setup interrupt event. */ - this->CreateInterruptEvent(bus); - } -} - -void I2cBusAccessor::Close() { - std::scoped_lock lk(this->open_mutex); - /* Close current session. */ - this->open_sessions--; - if (this->open_sessions > 0) { - return; - } - - /* Close interrupt event. */ - eventClose(&this->interrupt_event); - - /* Close PCV. */ - Pcv::Finalize(); - - this->suspended = false; -} - -void I2cBusAccessor::Suspend() { - std::scoped_lock lk(this->open_mutex); - std::scoped_lock lk_reg(this->register_mutex); - - if (!this->suspended) { - this->suspended = true; - - if (this->pcv_module != PcvModule_I2C5) { - this->DisableClock(); - } - } -} - -void I2cBusAccessor::Resume() { - if (this->suspended) { - this->DoInitialConfig(); - this->suspended = false; - } -} - -void I2cBusAccessor::DoInitialConfig() { - std::scoped_lock lk(this->register_mutex); - - if (this->pcv_module != PcvModule_I2C5) { - Pcv::Initialize(); - } - - this->ResetController(); - this->SetClock(this->speed_mode); - this->SetPacketMode(); - this->FlushFifos(); -} - -size_t I2cBusAccessor::GetOpenSessions() const { - return this->open_sessions; -} - -bool I2cBusAccessor::GetBusy() const { - /* Nintendo has a loop here that calls a member function to check if busy, retrying a few times. */ - /* This member function does "return false". */ - /* We will not bother with the loop. */ - return false; -} - -void I2cBusAccessor::OnStartTransaction() const { - /* Nothing actually happens here. */ -} - -void I2cBusAccessor::OnStopTransaction() const { - /* Nothing actually happens here. */ -} - -Result I2cBusAccessor::StartTransaction(DriverCommand command, AddressingMode addressing_mode, u32 slave_address) { - /* Nothing actually happens here... */ - return ResultSuccess; -} - -Result I2cBusAccessor::Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { - std::scoped_lock lk(this->register_mutex); - const u8 *cur_src = data; - size_t remaining = num_bytes; - - /* Set interrupt enable, clear interrupt status. */ - WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8E); - WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); - - ON_SCOPE_EXIT { this->ClearInterruptMask(); }; - - /* Send header. */ - this->WriteTransferHeader(TransferMode_Send, option, addressing_mode, slave_address, num_bytes); - - /* Send bytes. */ - while (true) { - const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0); - const size_t fifo_cnt = (fifo_status >> 4); - - for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - u32 val = 0; - for (size_t i = 0; i < cur_bytes; i++) { - val |= cur_src[i] << (8 * i); - } - WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val); - - cur_src += cur_bytes; - remaining -= cur_bytes; - } - - if (remaining == 0) { - break; - } - - eventClear(&this->interrupt_event); - if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { - this->HandleTransactionResult(ResultI2cBusBusy); - eventClear(&this->interrupt_event); - return ResultI2cTimedOut; - } - - R_TRY(this->GetAndHandleTransactionResult()); - } - - WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C); - - /* Wait for successful completion. */ - while (true) { - R_TRY(this->GetAndHandleTransactionResult()); - - /* Check PACKET_XFER_COMPLETE */ - const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); - if (interrupt_status & 0x80) { - R_TRY(this->GetAndHandleTransactionResult()); - break; - } - - eventClear(&this->interrupt_event); - if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { - this->HandleTransactionResult(ResultI2cBusBusy); - eventClear(&this->interrupt_event); - return ResultI2cTimedOut; - } - } - - return ResultSuccess; -} - -Result I2cBusAccessor::Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { - std::scoped_lock lk(this->register_mutex); - u8 *cur_dst = out_data; - size_t remaining = num_bytes; - - /* Set interrupt enable, clear interrupt status. */ - WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8D); - WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); - - /* Send header. */ - this->WriteTransferHeader(TransferMode_Receive, option, addressing_mode, slave_address, num_bytes); - - /* Receive bytes. */ - while (remaining > 0) { - eventClear(&this->interrupt_event); - if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) { - this->HandleTransactionResult(ResultI2cBusBusy); - this->ClearInterruptMask(); - eventClear(&this->interrupt_event); - return ResultI2cTimedOut; - } - - R_TRY(this->GetAndHandleTransactionResult()); - - const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0); - const size_t fifo_cnt = std::min((remaining + 3) >> 2, static_cast(fifo_status & 0xF)); - - for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { - const u32 val = ReadRegister(&this->i2c_registers->I2C_I2C_RX_FIFO_0); - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - for (size_t i = 0; i < cur_bytes; i++) { - cur_dst[i] = static_cast((val >> (8 * i)) & 0xFF); - } - - cur_dst += cur_bytes; - remaining -= cur_bytes; - } - } - - /* N doesn't do ClearInterruptMask. */ - return ResultSuccess; -} - -void I2cBusAccessor::SetBus(I2cBus bus) { - this->bus = bus; - this->i2c_registers = GetI2cRegisters(bus); - this->clkrst_registers.SetBus(bus); -} - -void I2cBusAccessor::CreateInterruptEvent(I2cBus bus) { - static constexpr u64 s_interrupts[] = { - 0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F - }; - if (static_cast(bus) >= sizeof(s_interrupts) / sizeof(s_interrupts[0])) { - std::abort(); - } - - Handle evt_h; - if (R_FAILED(svcCreateInterruptEvent(&evt_h, s_interrupts[static_cast(bus)], 1))) { - std::abort(); - } - - eventLoadRemote(&this->interrupt_event, evt_h, false); -} - -void I2cBusAccessor::SetClock(SpeedMode speed_mode) { - u32 t_high, t_low; - u32 clk_div, src_div; - u32 debounce; - - switch (speed_mode) { - case SpeedMode_Normal: - t_high = 2; - t_low = 4; - clk_div = 0x19; - src_div = 0x13; - debounce = 2; - break; - case SpeedMode_Fast: - t_high = 2; - t_low = 4; - clk_div = 0x19; - src_div = 0x04; - debounce = 2; - break; - case SpeedMode_FastPlus: - t_high = 2; - t_low = 4; - clk_div = 0x10; - src_div = 0x02; - debounce = 0; - break; - case SpeedMode_HighSpeed: - t_high = 3; - t_low = 8; - clk_div = 0x02; - src_div = 0x02; - debounce = 0; - break; - default: - std::abort(); - } - - if (speed_mode == SpeedMode_HighSpeed) { - WriteRegister(&this->i2c_registers->I2C_I2C_HS_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); - WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, clk_div); - } else { - WriteRegister(&this->i2c_registers->I2C_I2C_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); - WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, (clk_div << 16)); - } - - WriteRegister(&this->i2c_registers->I2C_I2C_CNFG_0, debounce); - ReadRegister(&this->i2c_registers->I2C_I2C_CNFG_0); - - if (this->pcv_module != PcvModule_I2C5) { - if (R_FAILED(Pcv::SetReset(this->pcv_module, true))) { - std::abort(); - } - if (R_FAILED(Pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)))) { - std::abort(); - } - if (R_FAILED(Pcv::SetReset(this->pcv_module, false))) { - std::abort(); - } - } -} - -void I2cBusAccessor::ResetController() const { - if (this->pcv_module != PcvModule_I2C5) { - if (R_FAILED(Pcv::SetReset(this->pcv_module, true))) { - std::abort(); - } - if (R_FAILED(Pcv::SetClockRate(this->pcv_module, 81'600'000))) { - std::abort(); - } - if (R_FAILED(Pcv::SetReset(this->pcv_module, false))) { - std::abort(); - } - } -} - -void I2cBusAccessor::ClearBus() const { - bool success = false; - for (size_t i = 0; i < 3 && !success; i++) { - success = true; - - this->ResetController(); - - WriteRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x90000); - SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x4); - SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x2); - - SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); - { - u64 start_tick = armGetSystemTick(); - while (ReadRegister(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - - SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1); - { - u64 start_tick = armGetSystemTick(); - while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - - { - u64 start_tick = armGetSystemTick(); - while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - } -} - -void I2cBusAccessor::DisableClock() { - if (R_FAILED(Pcv::SetClockEnabled(this->pcv_module, false))) { - std::abort(); - } -} - -void I2cBusAccessor::SetPacketMode() { - /* Set PACKET_MODE_EN, MSTR_CONFIG_LOAD */ - SetRegisterBits(&this->i2c_registers->I2C_I2C_CNFG_0, 0x400); - SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); - - /* Set TX_FIFO_TRIGGER, RX_FIFO_TRIGGER */ - WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC); -} - -Result I2cBusAccessor::FlushFifos() { - WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFF); - - /* Wait for flush to finish, check every ms for 5 ms. */ - for (size_t i = 0; i < 5; i++) { - if (!(ReadRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3)) { - return ResultSuccess; - } - svcSleepThread(1'000'000ul); - } - - return ResultI2cBusBusy; -} - -Result I2cBusAccessor::GetTransactionResult() const { - const u32 packet_status = ReadRegister(&this->i2c_registers->I2C_PACKET_TRANSFER_STATUS_0); - const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); - - /* Check for no ack. */ - if ((packet_status & 0xC) || (interrupt_status & 0x8)) { - return ResultI2cNoAck; - } - - /* Check for arb lost. */ - if ((packet_status & 0x2) || (interrupt_status & 0x4)) { - this->ClearBus(); - return ResultI2cBusBusy; - } - - return ResultSuccess; -} - -void I2cBusAccessor::HandleTransactionResult(Result result) { - if (R_FAILED(result)) { - if (result == ResultI2cNoAck || result == ResultI2cBusBusy) { - this->ResetController(); - this->SetClock(this->speed_mode); - this->SetPacketMode(); - this->FlushFifos(); - } else { - std::abort(); - } - } -} - -Result I2cBusAccessor::GetAndHandleTransactionResult() { - const Result transaction_res = this->GetTransactionResult(); - R_TRY_CLEANUP(transaction_res, { - this->HandleTransactionResult(transaction_res); - this->ClearInterruptMask(); - eventClear(&this->interrupt_event); - }); - return ResultSuccess; -} - -void I2cBusAccessor::WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes) { - this->FlushFifos(); - - WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, 0x10); - WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, static_cast(num_bytes - 1) & 0xFFF); - - const u32 slave_addr_val = ((transfer_mode == TransferMode_Receive) & 1) | ((slave_address & 0x7F) << 1); - u32 hdr_val = 0; - hdr_val |= ((this->speed_mode == SpeedMode_HighSpeed) & 1) << 22; - hdr_val |= ((transfer_mode == TransferMode_Receive) & 1) << 19; - hdr_val |= ((addressing_mode != AddressingMode_7Bit) & 1) << 18; - hdr_val |= (1 << 17); - hdr_val |= (((option & I2cTransactionOption_Stop) == 0) & 1) << 16; - hdr_val |= slave_addr_val; - - WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val); -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.hpp b/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.hpp deleted file mode 100644 index 322c0a457..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_bus_accessor.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" -#include "i2c_registers.hpp" - -class I2cBusAccessor { - private: - enum TransferMode { - TransferMode_Send = 0, - TransferMode_Receive = 1, - }; - static constexpr u64 InterruptTimeout = 100'000'000ul; - private: - Event interrupt_event; - HosMutex open_mutex; - HosMutex register_mutex; - I2cRegisters *i2c_registers = nullptr; - ClkRstRegisters clkrst_registers; - SpeedMode speed_mode = SpeedMode_Fast; - size_t open_sessions = 0; - I2cBus bus = I2cBus_I2c1; - PcvModule pcv_module = PcvModule_I2C1; - bool suspended = false; - public: - I2cBusAccessor() { - /* ... */ - } - private: - inline void ClearInterruptMask() const { - WriteRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0); - ReadRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0); - } - - void SetBus(I2cBus bus); - void CreateInterruptEvent(I2cBus bus); - void SetClock(SpeedMode speed_mode); - - void ResetController() const; - void ClearBus() const; - void DisableClock(); - void SetPacketMode(); - Result FlushFifos(); - - Result GetTransactionResult() const; - void HandleTransactionResult(Result result); - Result GetAndHandleTransactionResult(); - - void WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes); - public: - void Open(I2cBus bus, SpeedMode speed_mode); - void Close(); - void Suspend(); - void Resume(); - void DoInitialConfig(); - - size_t GetOpenSessions() const; - bool GetBusy() const; - - void OnStartTransaction() const; - Result StartTransaction(DriverCommand command, AddressingMode addressing_mode, u32 slave_address); - Result Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); - Result Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); - void OnStopTransaction() const; -}; diff --git a/stratosphere/boot/source/i2c_driver/i2c_command_list.cpp b/stratosphere/boot/source/i2c_driver/i2c_command_list.cpp deleted file mode 100644 index e57a75060..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_command_list.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_command_list.hpp" - -Result I2cCommandListFormatter::CanEnqueue(size_t size) const { - if (this->cmd_list_size - this->cur_index < size) { - return ResultI2cFullCommandList; - } - return ResultSuccess; -} - -Result I2cCommandListFormatter::EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size) { - R_TRY(this->CanEnqueue(SendCommandSize + size)); - - this->cmd_list[this->cur_index] = I2cCommand_Send; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; - this->cur_index++; - - this->cmd_list[this->cur_index++] = size; - - const u8 *src_u8 = reinterpret_cast(src); - for (size_t i = 0; i < size; i++) { - this->cmd_list[this->cur_index++] = src_u8[i]; - } - return ResultSuccess; -} - -Result I2cCommandListFormatter::EnqueueReceiveCommand(I2cTransactionOption option, size_t size) { - R_TRY(this->CanEnqueue(ReceiveCommandSize)); - - this->cmd_list[this->cur_index] = I2cCommand_Receive; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; - this->cur_index++; - - this->cmd_list[this->cur_index++] = size; - return ResultSuccess; -} - -Result I2cCommandListFormatter::EnqueueSleepCommand(size_t us) { - R_TRY(this->CanEnqueue(SleepCommandSize)); - - this->cmd_list[this->cur_index] = I2cCommand_SubCommand; - this->cmd_list[this->cur_index] |= I2cSubCommand_Sleep << 2; - this->cur_index++; - - this->cmd_list[this->cur_index++] = us; - return ResultSuccess; -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_command_list.hpp b/stratosphere/boot/source/i2c_driver/i2c_command_list.hpp deleted file mode 100644 index 8b88c588e..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_command_list.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" - -class I2cCommandListFormatter { - public: - static constexpr size_t MaxCommandListSize = 0x100; - static constexpr size_t SendCommandSize = 2; - static constexpr size_t ReceiveCommandSize = 2; - static constexpr size_t SleepCommandSize = 2; - private: - u8 *cmd_list = nullptr; - size_t cmd_list_size = 0; - size_t cur_index = 0; - public: - I2cCommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast(cmd_list)), cmd_list_size(cmd_list_size) { - if (cmd_list_size > MaxCommandListSize) { - std::abort(); - } - } - ~I2cCommandListFormatter() { - this->cmd_list = nullptr; - } - - private: - Result CanEnqueue(size_t size) const; - public: - size_t GetCurrentSize() const { - return this->cur_index; - } - - Result EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size); - Result EnqueueReceiveCommand(I2cTransactionOption option, size_t size); - Result EnqueueSleepCommand(size_t us); -}; diff --git a/stratosphere/boot/source/i2c_driver/i2c_device_config.cpp b/stratosphere/boot/source/i2c_driver/i2c_device_config.cpp deleted file mode 100644 index 73922d2bd..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_device_config.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_types.hpp" - -struct DeviceConfig { - I2cDevice device; - I2cBus bus; - u32 slave_address; - AddressingMode addressing_mode; - SpeedMode speed_mode; - u32 max_retries; - u64 retry_wait_time; -}; - -static constexpr DeviceConfig g_device_configs[I2cDevice_Count] = { - {I2cDevice_DebugPad, I2cBus_I2c1, 0x52, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_TouchPanel, I2cBus_I2c3, 0x49, AddressingMode_7Bit, SpeedMode_Fast, 0, 0}, - {I2cDevice_Tmp451, I2cBus_I2c1, 0x4c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_Nct72, I2cBus_I2c1, 0x4c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_Alc5639, I2cBus_I2c1, 0x1c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_Max77620Rtc, I2cBus_I2c5, 0x68, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Max77620Pmic, I2cBus_I2c5, 0x3c, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Max77621Cpu, I2cBus_I2c5, 0x1b, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Max77621Gpu, I2cBus_I2c5, 0x1c, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Bq24193, I2cBus_I2c1, 0x6b, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000}, - {I2cDevice_Max17050, I2cBus_I2c1, 0x36, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000}, - {I2cDevice_Bm92t30mwv, I2cBus_I2c1, 0x18, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000}, - {I2cDevice_Ina226Vdd15v0Hb, I2cBus_I2c2, 0x40, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysCpuDs, I2cBus_I2c2, 0x41, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysGpuDs, I2cBus_I2c2, 0x44, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysDdrDs, I2cBus_I2c2, 0x45, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysAp, I2cBus_I2c2, 0x46, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysBlDs, I2cBus_I2c2, 0x47, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Bh1730, I2cBus_I2c2, 0x29, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysCore, I2cBus_I2c2, 0x48, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226Soc1V8, I2cBus_I2c2, 0x49, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226Lpddr1V8, I2cBus_I2c2, 0x4a, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226Reg1V32, I2cBus_I2c2, 0x4b, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_Ina226Vdd3V3Sys, I2cBus_I2c2, 0x4d, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, - {I2cDevice_HdmiDdc, I2cBus_I2c4, 0x50, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_HdmiScdc, I2cBus_I2c4, 0x54, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_HdmiHdcp, I2cBus_I2c4, 0x3a, AddressingMode_7Bit, SpeedMode_Normal, 0, 0}, - {I2cDevice_Fan53528, I2cBus_I2c5, 0xa4, AddressingMode_7Bit, SpeedMode_Fast, 0, 0}, - {I2cDevice_Max77812_3, I2cBus_I2c5, 0x31, AddressingMode_7Bit, SpeedMode_Fast, 0, 0}, - {I2cDevice_Max77812_2, I2cBus_I2c5, 0x33, AddressingMode_7Bit, SpeedMode_Fast, 0, 0}, - {I2cDevice_Ina226VddDdr0V6, I2cBus_I2c2, 0x4e, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000}, -}; - -static constexpr size_t NumDeviceConfigs = sizeof(g_device_configs) / sizeof(g_device_configs[0]); - -static constexpr size_t I2cDeviceInvalidIndex = static_cast(-1); - -static size_t GetI2cDeviceIndex(I2cDevice dev) { - for (size_t i = 0; i < NumDeviceConfigs; i++) { - if (g_device_configs[i].device == dev) { - return i; - } - } - return I2cDeviceInvalidIndex; -} - -bool IsI2cDeviceSupported(I2cDevice dev) { - return GetI2cDeviceIndex(dev) != I2cDeviceInvalidIndex; -} - -I2cBus GetI2cDeviceBus(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].bus; -} - -u32 GetI2cDeviceSlaveAddress(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].slave_address; -} - -AddressingMode GetI2cDeviceAddressingMode(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].addressing_mode; -} - -SpeedMode GetI2cDeviceSpeedMode(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].speed_mode; -} - -u32 GetI2cDeviceMaxRetries(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].max_retries; -} - -u64 GetI2cDeviceRetryWaitTime(I2cDevice dev) { - const size_t dev_idx = GetI2cDeviceIndex(dev); - if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); } - return g_device_configs[dev_idx].retry_wait_time; -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_driver_session.cpp b/stratosphere/boot/source/i2c_driver/i2c_driver_session.cpp deleted file mode 100644 index 711dda5a6..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_driver_session.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "i2c_driver_session.hpp" - -void I2cDriverSession::Open(I2cBus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, I2cBusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time){ - std::scoped_lock lk(this->bus_accessor_mutex); - if (!this->open) { - this->bus_accessor = bus_accessor; - this->bus = bus; - this->slave_address = slave_address; - this->addressing_mode = addr_mode; - this->max_retries = max_retries; - this->retry_wait_time = retry_wait_time; - this->bus_accessor->Open(this->bus, speed_mode); - this->open = true; - } -} - -void I2cDriverSession::Start(){ - std::scoped_lock lk(this->bus_accessor_mutex); - if (this->open) { - if (this->bus_accessor->GetOpenSessions() == 1) { - this->bus_accessor->DoInitialConfig(); - } - } -} - -void I2cDriverSession::Close(){ - std::scoped_lock lk(this->bus_accessor_mutex); - if (this->open) { - this->bus_accessor->Close(); - this->bus_accessor = nullptr; - this->open = false; - } -} - -bool I2cDriverSession::IsOpen() const{ - return this->open; -} - -Result I2cDriverSession::DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command){ - std::scoped_lock lk(this->bus_accessor_mutex); - - if (this->bus_accessor->GetBusy()) { - return ResultI2cBusBusy; - } - - this->bus_accessor->OnStartTransaction(); - ON_SCOPE_EXIT { this->bus_accessor->OnStopTransaction(); }; - - R_TRY(this->bus_accessor->StartTransaction(command, this->addressing_mode, this->slave_address)); - - switch (command) { - case DriverCommand_Send: - R_TRY(this->bus_accessor->Send(reinterpret_cast(src), num_bytes, option, this->addressing_mode, this->slave_address)); - break; - case DriverCommand_Receive: - R_TRY(this->bus_accessor->Receive(reinterpret_cast(dst), num_bytes, option, this->addressing_mode, this->slave_address)); - break; - default: - std::abort(); - } - - return ResultSuccess; -} - -Result I2cDriverSession::DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command){ - size_t i = 0; - while (true) { - R_TRY_CATCH(this->DoTransaction(dst, src, num_bytes, option, command)) { - R_CATCH(ResultI2cTimedOut) { - i++; - if (i <= this->max_retries) { - svcSleepThread(this->retry_wait_time); - continue; - } - return ResultI2cBusBusy; - } - } R_END_TRY_CATCH; - return ResultSuccess; - } -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_driver_session.hpp b/stratosphere/boot/source/i2c_driver/i2c_driver_session.hpp deleted file mode 100644 index 51ddd9e29..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_driver_session.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" -#include "i2c_bus_accessor.hpp" - -class I2cDriverSession { - private: - HosMutex bus_accessor_mutex; - I2cBusAccessor *bus_accessor = nullptr; - I2cBus bus = I2cBus_I2c1; - u32 slave_address = 0; - AddressingMode addressing_mode = AddressingMode_7Bit; - u32 max_retries = 0; - u64 retry_wait_time = 0; - bool open = false; - public: - I2cDriverSession() { - /* ... */ - } - public: - void Open(I2cBus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, I2cBusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time); - void Start(); - void Close(); - - bool IsOpen() const; - - Result DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command); - Result DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command); -}; diff --git a/stratosphere/boot/source/i2c_driver/i2c_registers.hpp b/stratosphere/boot/source/i2c_driver/i2c_registers.hpp deleted file mode 100644 index 7293804f1..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_registers.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" - -static inline uintptr_t GetIoMapping(u64 io_addr, u64 io_size) { - u64 vaddr; - u64 aligned_addr = (io_addr & ~0xFFFul); - u64 aligned_size = io_size + (io_addr - aligned_addr); - if (R_FAILED(svcQueryIoMapping(&vaddr, aligned_addr, aligned_size))) { - std::abort(); - } - return static_cast(vaddr + (io_addr - aligned_addr)); -} - -struct I2cRegisters { - volatile u32 I2C_I2C_CNFG_0; - volatile u32 I2C_I2C_CMD_ADDR0_0; - volatile u32 I2C_I2C_CMD_ADDR1_0; - volatile u32 I2C_I2C_CMD_DATA1_0; - volatile u32 I2C_I2C_CMD_DATA2_0; - volatile u32 _0x14; - volatile u32 _0x18; - volatile u32 I2C_I2C_STATUS_0; - volatile u32 I2C_I2C_SL_CNFG_0; - volatile u32 I2C_I2C_SL_RCVD_0; - volatile u32 I2C_I2C_SL_STATUS_0; - volatile u32 I2C_I2C_SL_ADDR1_0; - volatile u32 I2C_I2C_SL_ADDR2_0; - volatile u32 I2C_I2C_TLOW_SEXT_0; - volatile u32 _0x38; - volatile u32 I2C_I2C_SL_DELAY_COUNT_0; - volatile u32 I2C_I2C_SL_INT_MASK_0; - volatile u32 I2C_I2C_SL_INT_SOURCE_0; - volatile u32 I2C_I2C_SL_INT_SET_0; - volatile u32 _0x4C; - volatile u32 I2C_I2C_TX_PACKET_FIFO_0; - volatile u32 I2C_I2C_RX_FIFO_0; - volatile u32 I2C_PACKET_TRANSFER_STATUS_0; - volatile u32 I2C_FIFO_CONTROL_0; - volatile u32 I2C_FIFO_STATUS_0; - volatile u32 I2C_INTERRUPT_MASK_REGISTER_0; - volatile u32 I2C_INTERRUPT_STATUS_REGISTER_0; - volatile u32 I2C_I2C_CLK_DIVISOR_REGISTER_0; - volatile u32 I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; - volatile u32 I2C_I2C_INTERRUPT_SET_REGISTER_0; - volatile u32 I2C_I2C_SLV_TX_PACKET_FIFO_0; - volatile u32 I2C_I2C_SLV_RX_FIFO_0; - volatile u32 I2C_I2C_SLV_PACKET_STATUS_0; - volatile u32 I2C_I2C_BUS_CLEAR_CONFIG_0; - volatile u32 I2C_I2C_BUS_CLEAR_STATUS_0; - volatile u32 I2C_I2C_CONFIG_LOAD_0; - volatile u32 _0x90; - volatile u32 I2C_I2C_INTERFACE_TIMING_0_0; - volatile u32 I2C_I2C_INTERFACE_TIMING_1_0; - volatile u32 I2C_I2C_HS_INTERFACE_TIMING_0_0; - volatile u32 I2C_I2C_HS_INTERFACE_TIMING_1_0; -}; - -static inline I2cRegisters *GetI2cRegisters(I2cBus bus) { - static constexpr uintptr_t s_offsets[] = { - 0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100 - }; - if (bus >= sizeof(s_offsets) / sizeof(s_offsets[0])) { - std::abort(); - } - - const uintptr_t registers = GetIoMapping(0x7000c000ul, 0x2000) + s_offsets[static_cast(bus)]; - return reinterpret_cast(registers); -} - -struct ClkRstRegisters { - public: - volatile u32 *clk_src_reg; - volatile u32 *clk_en_reg; - volatile u32 *rst_reg; - u32 mask; - public: - void SetBus(I2cBus bus) { - static constexpr uintptr_t s_clk_src_offsets[] = { - 0x124, 0x198, 0x1b8, 0x3c4, 0x128, 0x65c - }; - static constexpr uintptr_t s_clk_en_offsets[] = { - 0x010, 0x014, 0x018, 0x360, 0x014, 0x280 - }; - static constexpr uintptr_t s_rst_offsets[] = { - 0x004, 0x008, 0x00c, 0x358, 0x008, 0x28c - }; - static constexpr size_t s_bit_shifts[] = { - 12, 22, 3, 7, 15, 6 - }; - if (bus >= sizeof(s_clk_src_offsets) / sizeof(s_clk_src_offsets[0])) { - std::abort(); - } - - const uintptr_t registers = GetIoMapping(0x60006000ul, 0x1000); - const size_t idx = static_cast(bus); - this->clk_src_reg = reinterpret_cast(registers + s_clk_src_offsets[idx]); - this->clk_en_reg = reinterpret_cast(registers + s_clk_en_offsets[idx]); - this->rst_reg = reinterpret_cast(registers + s_rst_offsets[idx]); - this->mask = (1u << s_bit_shifts[idx]); - } -}; - -static inline void WriteRegister(volatile u32 *reg, u32 val) { - *reg = val; -} - -static inline u32 ReadRegister(volatile u32 *reg) { - u32 val = *reg; - return val; -} - -static inline void SetRegisterBits(volatile u32 *reg, u32 mask) { - *reg |= mask; -} - -static inline void ClearRegisterBits(volatile u32 *reg, u32 mask) { - *reg &= mask; -} - -static inline void ReadWriteRegisterBits(volatile u32 *reg, u32 val, u32 mask) { - *reg = (*reg & (~mask)) | (val & mask); -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_resource_manager.cpp b/stratosphere/boot/source/i2c_driver/i2c_resource_manager.cpp deleted file mode 100644 index ad3f412e1..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_resource_manager.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "boot_pcv.hpp" -#include "i2c_resource_manager.hpp" - -void I2cResourceManager::Initialize() { - std::scoped_lock lk(this->initialize_mutex); - this->ref_cnt++; -} - -void I2cResourceManager::Finalize() { - std::scoped_lock lk(this->initialize_mutex); - if (this->ref_cnt == 0) { - std::abort(); - } - this->ref_cnt--; - if (this->ref_cnt > 0) { - return; - } - - { - std::scoped_lock sess_lk(this->session_open_mutex); - for (size_t i = 0; i < MaxDriverSessions; i++) { - this->sessions[i].Close(); - } - } -} - -size_t I2cResourceManager::GetFreeSessionId() const { - for (size_t i = 0; i < MaxDriverSessions; i++) { - if (!this->sessions[i].IsOpen()) { - return i; - } - } - - return InvalidSessionId; -} - -void I2cResourceManager::OpenSession(I2cSessionImpl *out_session, I2cBus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time) { - bool need_enable_ldo6 = false; - size_t session_id = InvalidSessionId; - /* Get, open session. */ - { - std::scoped_lock lk(this->session_open_mutex); - if (out_session == nullptr || bus >= MaxBuses) { - std::abort(); - } - - session_id = GetFreeSessionId(); - if (session_id == InvalidSessionId) { - std::abort(); - } - - - if ((bus == I2cBus_I2c2 || bus == I2cBus_I2c3) && (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() == 0 && this->bus_accessors[I2cBus_I2c3].GetOpenSessions() == 0)) { - need_enable_ldo6 = true; - } - - out_session->session_id = session_id; - out_session->bus = bus; - this->sessions[session_id].Open(bus, slave_address, addressing_mode, speed_mode, &this->bus_accessors[bus], max_retries, retry_wait_time); - } - - this->sessions[session_id].Start(); - if (need_enable_ldo6) { - Pcv::Initialize(); - if (R_FAILED(Pcv::SetVoltageValue(10, 2'900'000))) { - std::abort(); - } - if (R_FAILED(Pcv::SetVoltageEnabled(10, true))) { - std::abort(); - } - Pcv::Finalize(); - svcSleepThread(560'000ul); - } -} - -void I2cResourceManager::CloseSession(const I2cSessionImpl &session) { - bool need_disable_ldo6 = false; - /* Get, open session. */ - { - std::scoped_lock lk(this->session_open_mutex); - if (!this->sessions[session.session_id].IsOpen()) { - std::abort(); - } - - this->sessions[session.session_id].Close(); - - if ((session.bus == I2cBus_I2c2 || session.bus == I2cBus_I2c3) && (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() == 0 && this->bus_accessors[I2cBus_I2c3].GetOpenSessions() == 0)) { - need_disable_ldo6 = true; - } - } - - if (need_disable_ldo6) { - Pcv::Initialize(); - if (R_FAILED(Pcv::SetVoltageEnabled(10, false))) { - std::abort(); - } - Pcv::Finalize(); - } - -} - -void I2cResourceManager::SuspendBuses() { - if (this->ref_cnt == 0) { - std::abort(); - } - - if (!this->suspended) { - { - std::scoped_lock lk(this->session_open_mutex); - this->suspended = true; - for (size_t i = 0; i < MaxBuses; i++) { - if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { - this->bus_accessors[i].Suspend(); - } - } - } - Pcv::Initialize(); - if (R_FAILED(Pcv::SetVoltageEnabled(10, false))) { - std::abort(); - } - Pcv::Finalize(); - } -} - -void I2cResourceManager::ResumeBuses() { - if (this->ref_cnt == 0) { - std::abort(); - } - - if (this->suspended) { - if (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() > 0 || this->bus_accessors[I2cBus_I2c3].GetOpenSessions() > 0) { - Pcv::Initialize(); - if (R_FAILED(Pcv::SetVoltageValue(10, 2'900'000))) { - std::abort(); - } - if (R_FAILED(Pcv::SetVoltageEnabled(10, true))) { - std::abort(); - } - Pcv::Finalize(); - svcSleepThread(1'560'000ul); - } - { - std::scoped_lock lk(this->session_open_mutex); - for (size_t i = 0; i < MaxBuses; i++) { - if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { - this->bus_accessors[i].Resume(); - } - } - } - this->suspended = false; - } -} - -void I2cResourceManager::SuspendPowerBus() { - if (this->ref_cnt == 0) { - std::abort(); - } - std::scoped_lock lk(this->session_open_mutex); - - if (!this->power_bus_suspended) { - this->power_bus_suspended = true; - if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { - this->bus_accessors[PowerBusId].Suspend(); - } - } -} - -void I2cResourceManager::ResumePowerBus() { - if (this->ref_cnt == 0) { - std::abort(); - } - std::scoped_lock lk(this->session_open_mutex); - - if (this->power_bus_suspended) { - if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { - this->bus_accessors[PowerBusId].Resume(); - } - this->power_bus_suspended = false; - } -} diff --git a/stratosphere/boot/source/i2c_driver/i2c_resource_manager.hpp b/stratosphere/boot/source/i2c_driver/i2c_resource_manager.hpp deleted file mode 100644 index 0f355d43d..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_resource_manager.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "i2c_types.hpp" -#include "i2c_bus_accessor.hpp" -#include "i2c_driver_session.hpp" - -class I2cResourceManager { - public: - static constexpr size_t MaxDriverSessions = 40; - static constexpr size_t MaxBuses = 6; - static constexpr size_t PowerBusId = static_cast(I2cBus_I2c5); - static constexpr size_t InvalidSessionId = static_cast(-1); - private: - HosMutex initialize_mutex; - HosMutex session_open_mutex; - size_t ref_cnt = 0; - bool suspended = false; - bool power_bus_suspended = false; - I2cDriverSession sessions[MaxDriverSessions]; - I2cBusAccessor bus_accessors[MaxBuses]; - HosMutex transaction_mutexes[MaxBuses]; - public: - I2cResourceManager() { - /* ... */ - } - private: - size_t GetFreeSessionId() const; - public: - /* N uses a singleton here, we'll oblige. */ - static I2cResourceManager &GetInstance() { - static I2cResourceManager s_instance; - return s_instance; - } - - bool IsInitialized() const { - return this->ref_cnt > 0; - } - - I2cDriverSession& GetSession(size_t id) { - return this->sessions[id]; - } - - HosMutex& GetTransactionMutex(I2cBus bus) { - if (bus >= MaxBuses) { - std::abort(); - } - return this->transaction_mutexes[bus]; - } - - void Initialize(); - void Finalize(); - - void OpenSession(I2cSessionImpl *out_session, I2cBus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time); - void CloseSession(const I2cSessionImpl &session); - void SuspendBuses(); - void ResumeBuses(); - void SuspendPowerBus(); - void ResumePowerBus(); -}; - diff --git a/stratosphere/boot/source/i2c_driver/i2c_types.hpp b/stratosphere/boot/source/i2c_driver/i2c_types.hpp deleted file mode 100644 index 329edb085..000000000 --- a/stratosphere/boot/source/i2c_driver/i2c_types.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 - -enum AddressingMode { - AddressingMode_7Bit = 0, -}; - -enum SpeedMode { - SpeedMode_Normal = 100000, - SpeedMode_Fast = 400000, - SpeedMode_FastPlus = 1000000, - SpeedMode_HighSpeed = 3400000, -}; - -enum I2cBus { - I2cBus_I2c1 = 0, - I2cBus_I2c2 = 1, - I2cBus_I2c3 = 2, - I2cBus_I2c4 = 3, - I2cBus_I2c5 = 4, - I2cBus_I2c6 = 5, -}; - -enum DriverCommand { - DriverCommand_Send = 0, - DriverCommand_Receive = 1, -}; - -struct I2cSessionImpl { - I2cBus bus; - size_t session_id; -}; - -enum I2cCommand { - I2cCommand_Send = 0, - I2cCommand_Receive = 1, - I2cCommand_SubCommand = 2, - I2cCommand_Count, -}; - -enum I2cSubCommand { - I2cSubCommand_Sleep = 0, - I2cSubCommand_Count, -}; - -bool IsI2cDeviceSupported(I2cDevice dev); -I2cBus GetI2cDeviceBus(I2cDevice dev); -u32 GetI2cDeviceSlaveAddress(I2cDevice dev); -AddressingMode GetI2cDeviceAddressingMode(I2cDevice dev); -SpeedMode GetI2cDeviceSpeedMode(I2cDevice dev); -u32 GetI2cDeviceMaxRetries(I2cDevice dev); -u64 GetI2cDeviceRetryWaitTime(I2cDevice dev); diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 0c26276b2..2e36c24a0 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 0c26276b21e4cf0f7a0665c0280b3888ebfbd1af +Subproject commit 2e36c24a01f30f91873cfab208ffdfe13a18f097