mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
boot: finish i2c driver
This commit is contained in:
parent
55a8154691
commit
31c4c33042
7 changed files with 439 additions and 0 deletions
|
@ -19,6 +19,7 @@
|
|||
#include <stratosphere.hpp>
|
||||
|
||||
#include "boot_types.hpp"
|
||||
#include "i2c_driver/i2c_types.hpp"
|
||||
|
||||
class Boot {
|
||||
public:
|
||||
|
@ -39,4 +40,8 @@ class Boot {
|
|||
|
||||
/* SPL Utilities. */
|
||||
static HardwareType GetHardwareType();
|
||||
|
||||
/* 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);
|
||||
};
|
||||
|
|
71
stratosphere/boot/source/boot_i2c_utils.cpp
Normal file
71
stratosphere/boot/source/boot_i2c_utils.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "boot_functions.hpp"
|
||||
#include "i2c_driver/i2c_api.hpp"
|
||||
|
||||
template<typename F>
|
||||
static Result RetryUntilSuccess(F f) {
|
||||
constexpr u64 timeout = 10'000'000'000ul;
|
||||
constexpr u64 retry_interval = 20'000'000ul;
|
||||
|
||||
u64 cur_time = 0;
|
||||
while (true) {
|
||||
Result rc = f();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
return rc;
|
||||
} else {
|
||||
cur_time += retry_interval;
|
||||
if (cur_time >= timeout) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
svcSleepThread(retry_interval);
|
||||
}
|
||||
}
|
||||
|
||||
Result Boot::ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
|
||||
Result rc;
|
||||
if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
u8 cmd_list[I2cCommandListFormatter::MaxCommandListSize];
|
||||
|
||||
I2cCommandListFormatter formatter(cmd_list, sizeof(cmd_list));
|
||||
if (R_FAILED((rc = formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size)))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED((rc = formatter.EnqueueReceiveCommand(static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size)))) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return RetryUntilSuccess([&]() { return I2cDriver::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) {
|
||||
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return RetryUntilSuccess([&]() { return I2cDriver::Send(session, cmd_list, src_size + cmd_size, static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); });
|
||||
}
|
186
stratosphere/boot/source/i2c_driver/i2c_api.cpp
Normal file
186
stratosphere/boot/source/i2c_driver/i2c_api.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
Result rc = I2cDriver::Send(session, *cur_cmd, num_bytes, option);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
(*cur_cmd) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result I2cReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
|
||||
I2cTransactionOption option = static_cast<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
Result rc = I2cDriver::Receive(session, *cur_dst, num_bytes, option);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
(*cur_dst) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result I2cSubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
|
||||
const I2cSubCommand sub_cmd = static_cast<I2cSubCommand>((**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<HosMutex &> 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<HosMutex &> 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<u8 *>(dst);
|
||||
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
|
||||
const u8 *cmd_list_end = cur_cmd + cmd_list_size;
|
||||
|
||||
while (cur_cmd < cmd_list_end) {
|
||||
I2cCommand cmd = static_cast<I2cCommand>((*cur_cmd) & 3);
|
||||
if (cmd >= I2cCommand_Count) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Result rc = g_cmd_handlers[cmd](&cur_cmd, &cur_dst, session);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void I2cDriver::SuspendBuses() {
|
||||
GetResourceManager().SuspendBuses();
|
||||
}
|
||||
|
||||
void I2cDriver::ResumeBuses() {
|
||||
GetResourceManager().ResumeBuses();
|
||||
}
|
||||
|
||||
void I2cDriver::SuspendPowerBus() {
|
||||
GetResourceManager().SuspendPowerBus();
|
||||
}
|
||||
|
||||
void I2cDriver::ResumePowerBus() {
|
||||
GetResourceManager().ResumePowerBus();
|
||||
}
|
38
stratosphere/boot/source/i2c_driver/i2c_api.hpp
Normal file
38
stratosphere/boot/source/i2c_driver/i2c_api.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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();
|
||||
};
|
74
stratosphere/boot/source/i2c_driver/i2c_command_list.cpp
Normal file
74
stratosphere/boot/source/i2c_driver/i2c_command_list.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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) {
|
||||
Result rc = this->CanEnqueue(SendCommandSize + size);
|
||||
if (R_FAILED(rc)) { return rc; }
|
||||
|
||||
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<const u8 *>(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) {
|
||||
Result rc = this->CanEnqueue(ReceiveCommandSize);
|
||||
if (R_FAILED(rc)) { return rc; }
|
||||
|
||||
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) {
|
||||
Result rc = this->CanEnqueue(SleepCommandSize);
|
||||
if (R_FAILED(rc)) { return rc; }
|
||||
|
||||
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;
|
||||
}
|
53
stratosphere/boot/source/i2c_driver/i2c_command_list.hpp
Normal file
53
stratosphere/boot/source/i2c_driver/i2c_command_list.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<u8 *>(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);
|
||||
};
|
|
@ -48,6 +48,18 @@ struct I2cSessionImpl {
|
|||
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);
|
||||
|
|
Loading…
Reference in a new issue