From 258cfb62a2b3b235ec993fad41b6ef6aa31a3d63 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 31 Oct 2020 22:52:43 -0700 Subject: [PATCH] i2c: command list format, get boot down to linker errors --- .../include/stratosphere/i2c.hpp | 1 + .../i2c/i2c_command_list_formatter.hpp | 51 ++++++++++ .../source/i2c/i2c_command_list_formatter.cpp | 95 +++++++++++++++++++ .../i2c/impl/i2c_command_list_format.hpp | 53 +++++++++++ .../include/vapours/results/i2c_results.hpp | 2 +- .../boot/source/boot_battery_driver.hpp | 7 +- .../boot/source/boot_charger_driver.hpp | 6 +- stratosphere/boot/source/boot_display.cpp | 7 +- .../boot/source/boot_driver_management.cpp | 2 +- stratosphere/boot/source/boot_i2c_utils.cpp | 39 ++++---- stratosphere/boot/source/boot_i2c_utils.hpp | 8 +- stratosphere/boot/source/boot_pmic_driver.hpp | 6 +- stratosphere/boot/source/boot_rtc_driver.hpp | 6 +- 13 files changed, 233 insertions(+), 50 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp create mode 100644 libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp create mode 100644 libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp diff --git a/libraries/libstratosphere/include/stratosphere/i2c.hpp b/libraries/libstratosphere/include/stratosphere/i2c.hpp index dad6bc5b1..cff61261d 100644 --- a/libraries/libstratosphere/include/stratosphere/i2c.hpp +++ b/libraries/libstratosphere/include/stratosphere/i2c.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp new file mode 100644 index 000000000..8a0e83c2c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::i2c { + + constexpr inline size_t CommandListLengthMax = 0x100; + constexpr inline size_t CommandListReceiveCommandSize = 2; + constexpr inline size_t CommandListSendCommandSize = 2; + constexpr inline size_t CommandListSleepCommandSize = 2; + + class CommandListFormatter { + NON_COPYABLE(CommandListFormatter); + NON_MOVEABLE(CommandListFormatter); + private: + size_t current_index; + size_t command_list_length; + void *command_list; + private: + Result IsEnqueueAble(size_t sz) const; + public: + CommandListFormatter(void *p, size_t sz) : current_index(0), command_list_length(sz), command_list(p) { + AMS_ABORT_UNLESS(this->command_list_length <= CommandListLengthMax); + } + + ~CommandListFormatter() { this->command_list = nullptr; } + + size_t GetCurrentLength() const { return this->current_index; } + const void *GetListHead() const { return this->command_list; } + + Result EnqueueReceiveCommand(i2c::TransactionOption option, size_t size); + Result EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size); + Result EnqueueSleepCommand(int us); + }; + +} diff --git a/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp b/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp new file mode 100644 index 000000000..eaf6cbd33 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/i2c_command_list_format.hpp" + +namespace ams::i2c { + + Result CommandListFormatter::IsEnqueueAble(size_t sz) const { + R_UNLESS(this->command_list_length - this->current_index >= sz, ResultCommandListFull()); + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueReceiveCommand(i2c::TransactionOption option, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Receive); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength + size)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Send); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + /* Copy the data we're sending. */ + std::memcpy(cmd_list + this->current_index, src, size); + this->current_index += size; + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSleepCommand(int us) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Extension); + header0.Set(impl::SubCommandId_Sleep); + + header1.Set(us); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp b/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp new file mode 100644 index 000000000..c4f4ad923 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::i2c::impl { + + enum CommandId { + CommandId_Send = 0, + CommandId_Receive = 1, + CommandId_Extension = 2, + CommandId_Count = 3, + }; + + enum SubCommandId { + SubCommandId_Sleep = 0, + }; + + struct CommonCommandFormat { + using CommandId = util::BitPack8::Field<0, 2>; + using SubCommandId = util::BitPack8::Field<2, 6>; + }; + + struct ReceiveCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SendCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SleepCommandFormat { + using MicroSeconds = util::BitPack8::Field<0, 8>; + }; + +} diff --git a/libraries/libvapours/include/vapours/results/i2c_results.hpp b/libraries/libvapours/include/vapours/results/i2c_results.hpp index 6204723ee..c1aa308af 100644 --- a/libraries/libvapours/include/vapours/results/i2c_results.hpp +++ b/libraries/libvapours/include/vapours/results/i2c_results.hpp @@ -23,7 +23,7 @@ namespace ams::i2c { R_DEFINE_ERROR_RESULT(NoAck, 1); R_DEFINE_ERROR_RESULT(BusBusy, 2); - R_DEFINE_ERROR_RESULT(FullCommandList, 3); + R_DEFINE_ERROR_RESULT(CommandListFull, 3); R_DEFINE_ERROR_RESULT(UnknownDevice, 5); diff --git a/stratosphere/boot/source/boot_battery_driver.hpp b/stratosphere/boot/source/boot_battery_driver.hpp index c5e6ab527..52a9c1a28 100644 --- a/stratosphere/boot/source/boot_battery_driver.hpp +++ b/stratosphere/boot/source/boot_battery_driver.hpp @@ -14,22 +14,19 @@ * along with this program. If not, see . */ #pragma once -#include "i2c/driver/i2c_api.hpp" namespace ams::boot { class BatteryDriver { private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: BatteryDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050); + i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050); } ~BatteryDriver() { i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); } private: Result Read(u8 addr, u16 *out_data); diff --git a/stratosphere/boot/source/boot_charger_driver.hpp b/stratosphere/boot/source/boot_charger_driver.hpp index 758b0d4a9..29d550928 100644 --- a/stratosphere/boot/source/boot_charger_driver.hpp +++ b/stratosphere/boot/source/boot_charger_driver.hpp @@ -23,11 +23,10 @@ namespace ams::boot { private: static constexpr u32 GpioPadName_Bq24193Charger = 0xA; private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: ChargerDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193); + i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193); //boot::gpio::Configure(GpioPadName_Bq24193Charger); //boot::gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output); @@ -35,7 +34,6 @@ namespace ams::boot { ~ChargerDriver() { i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); } private: Result Read(u8 addr, u8 *out_data); diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 2f44991ad..eb070b84d 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -196,11 +196,8 @@ namespace ams::boot { /* Turn on DSI/voltage rail. */ { - i2c::driver::Session i2c_session; - i2c::driver::Initialize(); - ON_SCOPE_EXIT { i2c::driver::Finalize(); }; - - i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic); + i2c::driver::I2cSession i2c_session; + i2c::driver::OpenSession(std::addressof(i2c_session), i2c::DeviceCode_Max77620Pmic); if (g_soc_type == spl::SocType_Mariko) { WriteI2cRegister(i2c_session, 0x18, 0x3A); diff --git a/stratosphere/boot/source/boot_driver_management.cpp b/stratosphere/boot/source/boot_driver_management.cpp index 77e0c907e..9fe63b017 100644 --- a/stratosphere/boot/source/boot_driver_management.cpp +++ b/stratosphere/boot/source/boot_driver_management.cpp @@ -46,7 +46,7 @@ namespace ams::boot { /* TODO: pwm::driver::board::Initialize(); */ /* Initialize the pwm driver library. */ - /* TODO: pwm::driver::Initialize(); + /* TODO: pwm::driver::Initialize(); */ } } diff --git a/stratosphere/boot/source/boot_i2c_utils.cpp b/stratosphere/boot/source/boot_i2c_utils.cpp index 541761b23..c996f6ef7 100644 --- a/stratosphere/boot/source/boot_i2c_utils.cpp +++ b/stratosphere/boot/source/boot_i2c_utils.cpp @@ -22,54 +22,51 @@ namespace ams::boot { template constexpr Result RetryUntilSuccess(F f) { - constexpr u64 timeout = 10'000'000'000ul; - constexpr u64 retry_interval = 20'000'000ul; + constexpr auto Timeout = TimeSpan::FromSeconds(10); + constexpr auto RetryInterval = TimeSpan::FromMilliSeconds(20); - u64 cur_time = 0; + TimeSpan cur_time = TimeSpan(0); while (true) { const auto retry_result = f(); R_SUCCEED_IF(R_SUCCEEDED(retry_result)); - cur_time += retry_interval; - if (cur_time < timeout) { - svcSleepThread(retry_interval); - continue; - } + cur_time += RetryInterval; + R_UNLESS(cur_time < Timeout, retry_result); - return retry_result; + os::SleepThread(RetryInterval); } } } - Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { + Result ReadI2cRegister(i2c::driver::I2cSession &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { AMS_ABORT_UNLESS(dst != nullptr && dst_size > 0); AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0); - u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize]; - + u8 cmd_list[i2c::CommandListLengthMax]; i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list)); - R_ABORT_UNLESS(formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size)); - R_ABORT_UNLESS(formatter.EnqueueReceiveCommand(static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size)); - return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); }); + R_ABORT_UNLESS(formatter.EnqueueSendCommand(i2c::TransactionOption_StartCondition, cmd, cmd_size)); + R_ABORT_UNLESS(formatter.EnqueueReceiveCommand(static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition), dst_size)); + + return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(dst, dst_size, session, cmd_list, formatter.GetCurrentLength()); }); } - Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { AMS_ABORT_UNLESS(src != nullptr && src_size > 0); AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0); u8 cmd_list[0x20]; /* N doesn't use a CommandListFormatter here... */ - std::memcpy(&cmd_list[0], cmd, cmd_size); - std::memcpy(&cmd_list[cmd_size], src, src_size); + std::memcpy(cmd_list + 0, cmd, cmd_size); + std::memcpy(cmd_list + cmd_size, src, src_size); - return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop)); }); + return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition)); }); } - Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value) { - return WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address)); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value) { + return WriteI2cRegister(session, std::addressof(value), sizeof(value), &address, sizeof(address)); } } diff --git a/stratosphere/boot/source/boot_i2c_utils.hpp b/stratosphere/boot/source/boot_i2c_utils.hpp index b069183e9..b2f328a4e 100644 --- a/stratosphere/boot/source/boot_i2c_utils.hpp +++ b/stratosphere/boot/source/boot_i2c_utils.hpp @@ -16,13 +16,11 @@ #pragma once #include -#include "i2c/driver/i2c_api.hpp" - namespace ams::boot { /* I2C Utilities. */ - Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); - Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); - Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value); + Result ReadI2cRegister(i2c::driver::I2cSession &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value); } diff --git a/stratosphere/boot/source/boot_pmic_driver.hpp b/stratosphere/boot/source/boot_pmic_driver.hpp index a9bb2f049..7d2a07f44 100644 --- a/stratosphere/boot/source/boot_pmic_driver.hpp +++ b/stratosphere/boot/source/boot_pmic_driver.hpp @@ -21,16 +21,14 @@ namespace ams::boot { /* Driver object. */ class PmicDriver { private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: PmicDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic); + i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Pmic); } ~PmicDriver() { i2c::driver::CloseSession(this->i2c_session); - 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 7225d350e..d40bb5949 100644 --- a/stratosphere/boot/source/boot_rtc_driver.hpp +++ b/stratosphere/boot/source/boot_rtc_driver.hpp @@ -20,16 +20,14 @@ namespace ams::boot { class RtcDriver { private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: RtcDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc); + R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Rtc)); } ~RtcDriver() { i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); } private: Result ReadRtcRegister(u8 *out, u8 address);