mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-03 08:08:39 -04:00
boot: refactor i2c driver into namespace
This commit is contained in:
parent
e62754ed56
commit
c87be7cd69
42 changed files with 1918 additions and 1836 deletions
188
stratosphere/boot/source/i2c/driver/i2c_api.cpp
Normal file
188
stratosphere/boot/source/i2c/driver/i2c_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<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)++;
|
||||
|
||||
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<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)++;
|
||||
|
||||
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<SubCommand>((**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<size_t>(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<HosMutex &> 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<HosMutex &> 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<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) {
|
||||
Command cmd = static_cast<Command>((*cur_cmd) & 3);
|
||||
if (cmd >= Command::Count) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
R_TRY(g_cmd_handlers[static_cast<size_t>(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();
|
||||
}
|
||||
|
||||
}
|
50
stratosphere/boot/source/i2c/driver/i2c_api.hpp
Normal file
50
stratosphere/boot/source/i2c/driver/i2c_api.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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();
|
||||
|
||||
}
|
478
stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
Normal file
478
stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> lk(this->open_mutex);
|
||||
std::scoped_lock<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<size_t>(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<u8>((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<u32>(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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
}
|
125
stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp
Normal file
125
stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<size_t>(-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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<size_t>(bus);
|
||||
}
|
||||
|
||||
constexpr inline Bus ConvertFromIndex(size_t idx) {
|
||||
if (idx >= static_cast<size_t>(Bus::Count)) {
|
||||
std::abort();
|
||||
}
|
||||
return static_cast<Bus>(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);
|
||||
|
||||
}
|
33
stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp
Normal file
33
stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/* 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 {
|
||||
|
||||
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);
|
||||
|
||||
}
|
129
stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp
Normal file
129
stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<volatile u32 *>(registers + s_clk_src_offsets[idx]);
|
||||
this->clk_en_reg = reinterpret_cast<volatile u32 *>(registers + s_clk_en_offsets[idx]);
|
||||
this->rst_reg = reinterpret_cast<volatile u32 *>(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 *>(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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "i2c_pcv.hpp"
|
||||
#include "i2c_resource_manager.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
void ResourceManager::Initialize() {
|
||||
std::scoped_lock<HosMutex> lk(this->initialize_mutex);
|
||||
this->ref_cnt++;
|
||||
}
|
||||
|
||||
void ResourceManager::Finalize() {
|
||||
std::scoped_lock<HosMutex> lk(this->initialize_mutex);
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
this->ref_cnt--;
|
||||
if (this->ref_cnt > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<size_t>(-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();
|
||||
};
|
||||
|
||||
}
|
103
stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp
Normal file
103
stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<HosMutex> 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<HosMutex> lk(this->bus_accessor_mutex);
|
||||
if (this->open) {
|
||||
if (this->bus_accessor->GetOpenSessions() == 1) {
|
||||
this->bus_accessor->DoInitialConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::Close() {
|
||||
std::scoped_lock<HosMutex> 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<HosMutex> 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<const u8 *>(src), num_bytes, option, this->addressing_mode, this->slave_address));
|
||||
break;
|
||||
case Command::Receive:
|
||||
R_TRY(this->bus_accessor->Receive(reinterpret_cast<u8 *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp
Normal file
51
stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue