i2c: command list format, get boot down to linker errors

This commit is contained in:
Michael Scire 2020-10-31 22:52:43 -07:00
parent 48784da42a
commit 43f7b10c0f
13 changed files with 233 additions and 50 deletions

View file

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <stratosphere/i2c/i2c_types.hpp> #include <stratosphere/i2c/i2c_types.hpp>
#include <stratosphere/i2c/i2c_select_device_name.hpp> #include <stratosphere/i2c/i2c_select_device_name.hpp>
#include <stratosphere/i2c/i2c_command_list_formatter.hpp>
#include <stratosphere/i2c/sf/i2c_sf_i_session.hpp> #include <stratosphere/i2c/sf/i2c_sf_i_session.hpp>
#include <stratosphere/i2c/sf/i2c_sf_i_manager.hpp> #include <stratosphere/i2c/sf/i2c_sf_i_manager.hpp>
#include <stratosphere/i2c/server/i2c_server_api.hpp> #include <stratosphere/i2c/server/i2c_server_api.hpp>

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/i2c/i2c_types.hpp>
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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<util::BitPack8 *>(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::CommonCommandFormat::CommandId>(impl::CommandId_Receive);
header0.Set<impl::ReceiveCommandFormat::StopCondition>((option & TransactionOption_StopCondition) != 0);
header0.Set<impl::ReceiveCommandFormat::StartCondition>((option & TransactionOption_StartCondition) != 0);
header1.Set<impl::ReceiveCommandFormat::Size>(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<util::BitPack8 *>(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::CommonCommandFormat::CommandId>(impl::CommandId_Send);
header0.Set<impl::SendCommandFormat::StopCondition>((option & TransactionOption_StopCondition) != 0);
header0.Set<impl::SendCommandFormat::StartCondition>((option & TransactionOption_StartCondition) != 0);
header1.Set<impl::SendCommandFormat::Size>(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<util::BitPack8 *>(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::CommonCommandFormat::CommandId>(impl::CommandId_Extension);
header0.Set<impl::CommonCommandFormat::SubCommandId>(impl::SubCommandId_Sleep);
header1.Set<impl::SleepCommandFormat::MicroSeconds>(us);
return ResultSuccess();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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>;
};
}

View file

@ -23,7 +23,7 @@ namespace ams::i2c {
R_DEFINE_ERROR_RESULT(NoAck, 1); R_DEFINE_ERROR_RESULT(NoAck, 1);
R_DEFINE_ERROR_RESULT(BusBusy, 2); R_DEFINE_ERROR_RESULT(BusBusy, 2);
R_DEFINE_ERROR_RESULT(FullCommandList, 3); R_DEFINE_ERROR_RESULT(CommandListFull, 3);
R_DEFINE_ERROR_RESULT(UnknownDevice, 5); R_DEFINE_ERROR_RESULT(UnknownDevice, 5);

View file

@ -14,22 +14,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "i2c/driver/i2c_api.hpp"
namespace ams::boot { namespace ams::boot {
class BatteryDriver { class BatteryDriver {
private: private:
i2c::driver::Session i2c_session; i2c::driver::I2cSession i2c_session;
public: public:
BatteryDriver() { BatteryDriver() {
i2c::driver::Initialize(); i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050);
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
} }
~BatteryDriver() { ~BatteryDriver() {
i2c::driver::CloseSession(this->i2c_session); i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
} }
private: private:
Result Read(u8 addr, u16 *out_data); Result Read(u8 addr, u16 *out_data);

View file

@ -23,11 +23,10 @@ namespace ams::boot {
private: private:
static constexpr u32 GpioPadName_Bq24193Charger = 0xA; static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
private: private:
i2c::driver::Session i2c_session; i2c::driver::I2cSession i2c_session;
public: public:
ChargerDriver() { ChargerDriver() {
i2c::driver::Initialize(); i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193);
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
//boot::gpio::Configure(GpioPadName_Bq24193Charger); //boot::gpio::Configure(GpioPadName_Bq24193Charger);
//boot::gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output); //boot::gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
@ -35,7 +34,6 @@ namespace ams::boot {
~ChargerDriver() { ~ChargerDriver() {
i2c::driver::CloseSession(this->i2c_session); i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
} }
private: private:
Result Read(u8 addr, u8 *out_data); Result Read(u8 addr, u8 *out_data);

View file

@ -196,11 +196,8 @@ namespace ams::boot {
/* Turn on DSI/voltage rail. */ /* Turn on DSI/voltage rail. */
{ {
i2c::driver::Session i2c_session; i2c::driver::I2cSession i2c_session;
i2c::driver::Initialize(); i2c::driver::OpenSession(std::addressof(i2c_session), i2c::DeviceCode_Max77620Pmic);
ON_SCOPE_EXIT { i2c::driver::Finalize(); };
i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
if (g_soc_type == spl::SocType_Mariko) { if (g_soc_type == spl::SocType_Mariko) {
WriteI2cRegister(i2c_session, 0x18, 0x3A); WriteI2cRegister(i2c_session, 0x18, 0x3A);

View file

@ -46,7 +46,7 @@ namespace ams::boot {
/* TODO: pwm::driver::board::Initialize(); */ /* TODO: pwm::driver::board::Initialize(); */
/* Initialize the pwm driver library. */ /* Initialize the pwm driver library. */
/* TODO: pwm::driver::Initialize(); /* TODO: pwm::driver::Initialize(); */
} }
} }

View file

@ -22,54 +22,51 @@ namespace ams::boot {
template<typename F> template<typename F>
constexpr Result RetryUntilSuccess(F f) { constexpr Result RetryUntilSuccess(F f) {
constexpr u64 timeout = 10'000'000'000ul; constexpr auto Timeout = TimeSpan::FromSeconds(10);
constexpr u64 retry_interval = 20'000'000ul; constexpr auto RetryInterval = TimeSpan::FromMilliSeconds(20);
u64 cur_time = 0; TimeSpan cur_time = TimeSpan(0);
while (true) { while (true) {
const auto retry_result = f(); const auto retry_result = f();
R_SUCCEED_IF(R_SUCCEEDED(retry_result)); R_SUCCEED_IF(R_SUCCEEDED(retry_result));
cur_time += retry_interval; cur_time += RetryInterval;
if (cur_time < timeout) { R_UNLESS(cur_time < Timeout, retry_result);
svcSleepThread(retry_interval);
continue;
}
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(dst != nullptr && dst_size > 0);
AMS_ABORT_UNLESS(cmd != nullptr && cmd_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)); 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>(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>(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(src != nullptr && src_size > 0);
AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0); AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0);
u8 cmd_list[0x20]; u8 cmd_list[0x20];
/* N doesn't use a CommandListFormatter here... */ /* N doesn't use a CommandListFormatter here... */
std::memcpy(&cmd_list[0], cmd, cmd_size); std::memcpy(cmd_list + 0, cmd, cmd_size);
std::memcpy(&cmd_list[cmd_size], src, src_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>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); }); return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast<i2c::TransactionOption>(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition)); });
} }
Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value) { Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value) {
return WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address)); return WriteI2cRegister(session, std::addressof(value), sizeof(value), &address, sizeof(address));
} }
} }

View file

@ -16,13 +16,11 @@
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
namespace ams::boot { namespace ams::boot {
/* I2C Utilities. */ /* I2C Utilities. */
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);
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);
Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value); Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value);
} }

View file

@ -21,16 +21,14 @@ namespace ams::boot {
/* Driver object. */ /* Driver object. */
class PmicDriver { class PmicDriver {
private: private:
i2c::driver::Session i2c_session; i2c::driver::I2cSession i2c_session;
public: public:
PmicDriver() { PmicDriver() {
i2c::driver::Initialize(); i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Pmic);
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
} }
~PmicDriver() { ~PmicDriver() {
i2c::driver::CloseSession(this->i2c_session); i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
} }
private: private:
Result GetPowerStatus(u8 *out); Result GetPowerStatus(u8 *out);

View file

@ -20,16 +20,14 @@ namespace ams::boot {
class RtcDriver { class RtcDriver {
private: private:
i2c::driver::Session i2c_session; i2c::driver::I2cSession i2c_session;
public: public:
RtcDriver() { RtcDriver() {
i2c::driver::Initialize(); R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Rtc));
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
} }
~RtcDriver() { ~RtcDriver() {
i2c::driver::CloseSession(this->i2c_session); i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
} }
private: private:
Result ReadRtcRegister(u8 *out, u8 address); Result ReadRtcRegister(u8 *out, u8 address);