|
|
|
|
@@ -55,56 +55,56 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm) {
|
|
|
|
|
AMS_ASSERT(this->state == State::NotInitialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::NotInitialized);
|
|
|
|
|
|
|
|
|
|
this->is_power_bus = pb;
|
|
|
|
|
this->speed_mode = sm;
|
|
|
|
|
this->interrupt_name = intr;
|
|
|
|
|
this->registers_phys_addr = reg_paddr;
|
|
|
|
|
this->registers_size = reg_size;
|
|
|
|
|
this->state = State::Initializing;
|
|
|
|
|
m_is_power_bus = pb;
|
|
|
|
|
m_speed_mode = sm;
|
|
|
|
|
m_interrupt_name = intr;
|
|
|
|
|
m_registers_phys_addr = reg_paddr;
|
|
|
|
|
m_registers_size = reg_size;
|
|
|
|
|
m_state = State::Initializing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::RegisterDeviceCode(DeviceCode dc) {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initializing);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initializing);
|
|
|
|
|
|
|
|
|
|
this->device_code = dc;
|
|
|
|
|
m_device_code = dc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::InitializeDriver() {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initializing);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initializing);
|
|
|
|
|
|
|
|
|
|
this->registers = reinterpret_cast<volatile I2cRegisters *>(dd::QueryIoMapping(this->registers_phys_addr, this->registers_size));
|
|
|
|
|
AMS_ABORT_UNLESS(this->registers != nullptr);
|
|
|
|
|
m_registers = reinterpret_cast<volatile I2cRegisters *>(dd::QueryIoMapping(m_registers_phys_addr, m_registers_size));
|
|
|
|
|
AMS_ABORT_UNLESS(m_registers != nullptr);
|
|
|
|
|
|
|
|
|
|
this->state = State::Initialized;
|
|
|
|
|
m_state = State::Initialized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::FinalizeDriver() {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
this->state = State::Initializing;
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
m_state = State::Initializing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::InitializeDevice(I2cDeviceProperty *device) {
|
|
|
|
|
/* Check that the device is valid. */
|
|
|
|
|
AMS_ASSERT(device != nullptr);
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
AMS_UNUSED(device);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* Increment our user count -- if we're already open, we're done. */
|
|
|
|
|
AMS_ASSERT(this->user_count >= 0);
|
|
|
|
|
++this->user_count;
|
|
|
|
|
R_SUCCEED_IF(this->user_count > 1);
|
|
|
|
|
AMS_ASSERT(m_user_count >= 0);
|
|
|
|
|
++m_user_count;
|
|
|
|
|
R_SUCCEED_IF(m_user_count > 1);
|
|
|
|
|
|
|
|
|
|
/* Initialize our interrupt event. */
|
|
|
|
|
os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear);
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
os::InitializeInterruptEvent(std::addressof(m_interrupt_event), m_interrupt_name, os::EventClearMode_ManualClear);
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
|
|
|
|
|
/* If we're not power bus, perform power management init. */
|
|
|
|
|
if (!this->is_power_bus) {
|
|
|
|
|
if (!m_is_power_bus) {
|
|
|
|
|
/* Initialize regulator library. */
|
|
|
|
|
regulator::Initialize();
|
|
|
|
|
|
|
|
|
|
@@ -112,9 +112,9 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
R_TRY(this->TryOpenRegulatorSession());
|
|
|
|
|
|
|
|
|
|
/* If we have a regulator session, set voltage to 2.9V. */
|
|
|
|
|
if (this->has_regulator_session) {
|
|
|
|
|
if (m_has_regulator_session) {
|
|
|
|
|
/* NOTE: Nintendo does not check the result, here. */
|
|
|
|
|
regulator::SetVoltageValue(std::addressof(this->regulator_session), 2'900'000u);
|
|
|
|
|
regulator::SetVoltageValue(std::addressof(m_regulator_session), 2'900'000u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize clock/reset library. */
|
|
|
|
|
@@ -125,12 +125,12 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
this->ExecuteInitialConfig();
|
|
|
|
|
|
|
|
|
|
/* If we have a regulator session, enable voltage. */
|
|
|
|
|
if (!this->is_power_bus && this->has_regulator_session) {
|
|
|
|
|
if (!m_is_power_bus && m_has_regulator_session) {
|
|
|
|
|
/* Check whether voltage was already enabled. */
|
|
|
|
|
const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session));
|
|
|
|
|
const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(m_regulator_session));
|
|
|
|
|
|
|
|
|
|
/* NOTE: Nintendo does not check the result of this call. */
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true);
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(m_regulator_session), true);
|
|
|
|
|
|
|
|
|
|
/* If we enabled voltage, delay to give our enable time to take. */
|
|
|
|
|
if (!was_enabled) {
|
|
|
|
|
@@ -144,35 +144,35 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
void I2cBusAccessor::FinalizeDevice(I2cDeviceProperty *device) {
|
|
|
|
|
/* Check that the device is valid. */
|
|
|
|
|
AMS_ASSERT(device != nullptr);
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
AMS_UNUSED(device);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* Increment our user count -- if we're not the last user, we're done. */
|
|
|
|
|
AMS_ASSERT(this->user_count > 0);
|
|
|
|
|
--this->user_count;
|
|
|
|
|
if (this->user_count > 0) {
|
|
|
|
|
AMS_ASSERT(m_user_count > 0);
|
|
|
|
|
--m_user_count;
|
|
|
|
|
if (m_user_count > 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finalize our interrupt event. */
|
|
|
|
|
os::FinalizeInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
os::FinalizeInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
|
|
|
|
|
/* If we have a regulator session, disable voltage. */
|
|
|
|
|
if (this->has_regulator_session) {
|
|
|
|
|
if (m_has_regulator_session) {
|
|
|
|
|
/* NOTE: Nintendo does not check the result of this call. */
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false);
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(m_regulator_session), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finalize the clock/reset library. */
|
|
|
|
|
clkrst::Finalize();
|
|
|
|
|
|
|
|
|
|
/* If we have a regulator session, close it. */
|
|
|
|
|
if (this->has_regulator_session) {
|
|
|
|
|
regulator::CloseSession(std::addressof(this->regulator_session));
|
|
|
|
|
this->has_regulator_session = false;
|
|
|
|
|
if (m_has_regulator_session) {
|
|
|
|
|
regulator::CloseSession(std::addressof(m_regulator_session));
|
|
|
|
|
m_has_regulator_session = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finalize the regulator library. */
|
|
|
|
|
@@ -185,10 +185,10 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
AMS_ASSERT(src != nullptr);
|
|
|
|
|
AMS_ASSERT(src_size > 0);
|
|
|
|
|
|
|
|
|
|
if (this->is_power_bus) {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended);
|
|
|
|
|
if (m_is_power_bus) {
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized || m_state == State::Suspended);
|
|
|
|
|
} else {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send the data. */
|
|
|
|
|
@@ -201,10 +201,10 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
AMS_ASSERT(dst != nullptr);
|
|
|
|
|
AMS_ASSERT(dst_size > 0);
|
|
|
|
|
|
|
|
|
|
if (this->is_power_bus) {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended);
|
|
|
|
|
if (m_is_power_bus) {
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized || m_state == State::Suspended);
|
|
|
|
|
} else {
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send the data. */
|
|
|
|
|
@@ -213,18 +213,18 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::SuspendBus() {
|
|
|
|
|
/* Check that state is valid. */
|
|
|
|
|
AMS_ASSERT(this->state == State::Initialized);
|
|
|
|
|
AMS_ASSERT(m_state == State::Initialized);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* If we need to, disable clock/voltage appropriately. */
|
|
|
|
|
if (!this->is_power_bus && this->user_count > 0) {
|
|
|
|
|
if (!m_is_power_bus && m_user_count > 0) {
|
|
|
|
|
/* Disable clock. */
|
|
|
|
|
{
|
|
|
|
|
/* Open a clkrst session. */
|
|
|
|
|
clkrst::ClkRstSession clkrst_session;
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code));
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), m_device_code));
|
|
|
|
|
ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); };
|
|
|
|
|
|
|
|
|
|
/* Set clock disabled for the session. */
|
|
|
|
|
@@ -232,47 +232,47 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Disable voltage. */
|
|
|
|
|
if (this->has_regulator_session) {
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false);
|
|
|
|
|
if (m_has_regulator_session) {
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(m_regulator_session), false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update state. */
|
|
|
|
|
this->state = State::Suspended;
|
|
|
|
|
m_state = State::Suspended;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::SuspendPowerBus() {
|
|
|
|
|
/* Check that state is valid. */
|
|
|
|
|
AMS_ASSERT(this->state == State::Suspended);
|
|
|
|
|
AMS_ASSERT(m_state == State::Suspended);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* If we need to, disable clock/voltage appropriately. */
|
|
|
|
|
if (this->is_power_bus && this->user_count > 0) {
|
|
|
|
|
if (m_is_power_bus && m_user_count > 0) {
|
|
|
|
|
/* Nothing should actually be done here. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update state. */
|
|
|
|
|
this->state = State::PowerBusSuspended;
|
|
|
|
|
m_state = State::PowerBusSuspended;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::ResumeBus() {
|
|
|
|
|
/* Check that state is valid. */
|
|
|
|
|
AMS_ASSERT(this->state == State::Suspended);
|
|
|
|
|
AMS_ASSERT(m_state == State::Suspended);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* If we need to, enable clock/voltage appropriately. */
|
|
|
|
|
if (!this->is_power_bus && this->user_count > 0) {
|
|
|
|
|
if (!m_is_power_bus && m_user_count > 0) {
|
|
|
|
|
/* Enable voltage. */
|
|
|
|
|
if (this->has_regulator_session) {
|
|
|
|
|
if (m_has_regulator_session) {
|
|
|
|
|
/* Check whether voltage was already enabled. */
|
|
|
|
|
const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session));
|
|
|
|
|
const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(m_regulator_session));
|
|
|
|
|
|
|
|
|
|
/* NOTE: Nintendo does not check the result of this call. */
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true);
|
|
|
|
|
regulator::SetVoltageEnabled(std::addressof(m_regulator_session), true);
|
|
|
|
|
|
|
|
|
|
/* If we enabled voltage, delay to give our enable time to take. */
|
|
|
|
|
if (!was_enabled) {
|
|
|
|
|
@@ -285,36 +285,36 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update state. */
|
|
|
|
|
this->state = State::Initialized;
|
|
|
|
|
m_state = State::Initialized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::ResumePowerBus() {
|
|
|
|
|
/* Check that state is valid. */
|
|
|
|
|
AMS_ASSERT(this->state == State::PowerBusSuspended);
|
|
|
|
|
AMS_ASSERT(m_state == State::PowerBusSuspended);
|
|
|
|
|
|
|
|
|
|
/* Acquire exclusive access. */
|
|
|
|
|
std::scoped_lock lk(this->user_count_mutex);
|
|
|
|
|
std::scoped_lock lk(m_user_count_mutex);
|
|
|
|
|
|
|
|
|
|
/* If we need to, enable clock/voltage appropriately. */
|
|
|
|
|
if (this->is_power_bus && this->user_count > 0) {
|
|
|
|
|
if (m_is_power_bus && m_user_count > 0) {
|
|
|
|
|
/* Execute initial config, which will enable clock as relevant. */
|
|
|
|
|
this->ExecuteInitialConfig();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update state. */
|
|
|
|
|
this->state = State::Suspended;
|
|
|
|
|
m_state = State::Suspended;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::TryOpenRegulatorSession() {
|
|
|
|
|
/* Ensure we track the session. */
|
|
|
|
|
this->has_regulator_session = true;
|
|
|
|
|
auto s_guard = SCOPE_GUARD { this->has_regulator_session = false; };
|
|
|
|
|
m_has_regulator_session = true;
|
|
|
|
|
auto s_guard = SCOPE_GUARD { m_has_regulator_session = false; };
|
|
|
|
|
|
|
|
|
|
/* Try to open the session. */
|
|
|
|
|
R_TRY_CATCH(regulator::OpenSession(std::addressof(this->regulator_session), this->device_code)) {
|
|
|
|
|
R_TRY_CATCH(regulator::OpenSession(std::addressof(m_regulator_session), m_device_code)) {
|
|
|
|
|
R_CATCH(ddsf::ResultDeviceCodeNotFound) {
|
|
|
|
|
/* It's okay if the device isn't found, but we don't have a session if so. */
|
|
|
|
|
this->has_regulator_session = false;
|
|
|
|
|
m_has_regulator_session = false;
|
|
|
|
|
}
|
|
|
|
|
} R_END_TRY_CATCH;
|
|
|
|
|
|
|
|
|
|
@@ -325,13 +325,13 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::ExecuteInitialConfig() {
|
|
|
|
|
/* Lock exclusive access to registers. */
|
|
|
|
|
std::scoped_lock lk(this->register_mutex);
|
|
|
|
|
std::scoped_lock lk(m_register_mutex);
|
|
|
|
|
|
|
|
|
|
/* Reset the controller. */
|
|
|
|
|
this->ResetController();
|
|
|
|
|
|
|
|
|
|
/* Set clock registers. */
|
|
|
|
|
this->SetClockRegisters(this->speed_mode);
|
|
|
|
|
this->SetClockRegisters(m_speed_mode);
|
|
|
|
|
|
|
|
|
|
/* Set packet mode registers. */
|
|
|
|
|
this->SetPacketModeRegisters();
|
|
|
|
|
@@ -342,20 +342,20 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
|
|
|
|
|
/* Acquire exclusive access to the registers. */
|
|
|
|
|
std::scoped_lock lk(this->register_mutex);
|
|
|
|
|
std::scoped_lock lk(m_register_mutex);
|
|
|
|
|
|
|
|
|
|
/* Configure interrupt mask, clear interrupt status. */
|
|
|
|
|
reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
reg::Write(m_registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
|
|
|
|
|
reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET));
|
|
|
|
|
reg::Write(m_registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET));
|
|
|
|
|
|
|
|
|
|
/* Write the header. */
|
|
|
|
|
this->WriteHeader(Xfer_Write, src_size, option, slave_address, addressing_mode);
|
|
|
|
|
@@ -366,7 +366,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
/* Get the number of empty bytes in the fifo status. */
|
|
|
|
|
const u32 empty = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_TX_FIFO_EMPTY_CNT));
|
|
|
|
|
const u32 empty = reg::GetValue(m_registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_TX_FIFO_EMPTY_CNT));
|
|
|
|
|
|
|
|
|
|
/* Write up to (empty) bytes to the fifo. */
|
|
|
|
|
for (u32 i = 0; remaining > 0 && i < empty; ++i) {
|
|
|
|
|
@@ -379,7 +379,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the data word. */
|
|
|
|
|
reg::Write(this->registers->tx_packet_fifo, word);
|
|
|
|
|
reg::Write(m_registers->tx_packet_fifo, word);
|
|
|
|
|
|
|
|
|
|
/* Advance. */
|
|
|
|
|
cur += cur_bytes;
|
|
|
|
|
@@ -392,13 +392,13 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for our current data to send. */
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) {
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(m_interrupt_event), Timeout)) {
|
|
|
|
|
/* We timed out. */
|
|
|
|
|
this->HandleTransactionError(i2c::ResultBusBusy());
|
|
|
|
|
|
|
|
|
|
this->DisableInterruptMask();
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
return i2c::ResultTimeout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -407,9 +407,9 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Configure interrupt mask to not care about tfifo data req. */
|
|
|
|
|
reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
reg::Write(m_registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
|
|
|
|
|
/* Wait for the packet transfer to complete. */
|
|
|
|
|
while (true) {
|
|
|
|
|
@@ -417,18 +417,18 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
R_TRY(this->CheckAndHandleError());
|
|
|
|
|
|
|
|
|
|
/* Check if packet transfer is done. */
|
|
|
|
|
if (reg::HasValue(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET))) {
|
|
|
|
|
if (reg::HasValue(m_registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET))) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for our the packet to transfer. */
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) {
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(m_interrupt_event), Timeout)) {
|
|
|
|
|
/* We timed out. */
|
|
|
|
|
this->HandleTransactionError(i2c::ResultBusBusy());
|
|
|
|
|
|
|
|
|
|
this->DisableInterruptMask();
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
return i2c::ResultTimeout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -443,20 +443,20 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
|
|
|
|
|
/* Acquire exclusive access to the registers. */
|
|
|
|
|
std::scoped_lock lk(this->register_mutex);
|
|
|
|
|
std::scoped_lock lk(m_register_mutex);
|
|
|
|
|
|
|
|
|
|
/* Configure interrupt mask, clear interrupt status. */
|
|
|
|
|
reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
reg::Write(m_registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE));
|
|
|
|
|
|
|
|
|
|
reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET));
|
|
|
|
|
reg::Write(m_registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET));
|
|
|
|
|
|
|
|
|
|
/* Write the header. */
|
|
|
|
|
this->WriteHeader(Xfer_Read, dst_size, option, slave_address, addressing_mode);
|
|
|
|
|
@@ -467,13 +467,13 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
while (remaining > 0) {
|
|
|
|
|
/* Wait for data to come in. */
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) {
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
if (!os::TimedWaitInterruptEvent(std::addressof(m_interrupt_event), Timeout)) {
|
|
|
|
|
/* We timed out. */
|
|
|
|
|
this->HandleTransactionError(i2c::ResultBusBusy());
|
|
|
|
|
|
|
|
|
|
this->DisableInterruptMask();
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
|
|
|
|
os::ClearInterruptEvent(std::addressof(m_interrupt_event));
|
|
|
|
|
return i2c::ResultTimeout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -481,7 +481,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
R_TRY(this->CheckAndHandleError());
|
|
|
|
|
|
|
|
|
|
/* Get the number of full bytes in the fifo status. */
|
|
|
|
|
const u32 full = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_RX_FIFO_FULL_CNT));
|
|
|
|
|
const u32 full = reg::GetValue(m_registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_RX_FIFO_FULL_CNT));
|
|
|
|
|
|
|
|
|
|
/* Determine how many words we can read. */
|
|
|
|
|
const size_t cur_words = std::min(util::DivideUp(remaining, sizeof(u32)), static_cast<size_t>(full));
|
|
|
|
|
@@ -489,7 +489,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
/* Read the correct number of words from the fifo. */
|
|
|
|
|
for (size_t i = 0; i < cur_words; ++i) {
|
|
|
|
|
/* Read the word from the fifo. */
|
|
|
|
|
const u32 word = reg::Read(this->registers->rx_fifo);
|
|
|
|
|
const u32 word = reg::Read(m_registers->rx_fifo);
|
|
|
|
|
|
|
|
|
|
/* Copy bytes from the word. */
|
|
|
|
|
const size_t cur_bytes = std::min(remaining, sizeof(u32));
|
|
|
|
|
@@ -512,41 +512,41 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
const bool is_read = xfer == Xfer_Read;
|
|
|
|
|
const bool is_7_bit = addressing_mode == AddressingMode_SevenBit;
|
|
|
|
|
const bool is_stop = (option & TransactionOption_StopCondition) != 0;
|
|
|
|
|
const bool is_hs = this->speed_mode == SpeedMode_HighSpeed;
|
|
|
|
|
const bool is_hs = m_speed_mode == SpeedMode_HighSpeed;
|
|
|
|
|
const u32 slave_addr = ((static_cast<u32>(slave_address) & 0x7F) << 1) | (is_read ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
/* Flush fifos. */
|
|
|
|
|
this->FlushFifos();
|
|
|
|
|
|
|
|
|
|
/* Enqueue the first header word. */
|
|
|
|
|
reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM (HEADER_WORD0_PROT_HDR_SZ, 1_WORD),
|
|
|
|
|
IO_PACKET_BITS_VALUE(HEADER_WORD0_PKT_ID, 0),
|
|
|
|
|
IO_PACKET_BITS_VALUE(HEADER_WORD0_CONTROLLER_ID, 0),
|
|
|
|
|
IO_PACKET_BITS_ENUM (HEADER_WORD0_PROTOCOL, I2C),
|
|
|
|
|
IO_PACKET_BITS_ENUM (HEADER_WORD0_PKT_TYPE, REQUEST));
|
|
|
|
|
reg::Write(m_registers->tx_packet_fifo, IO_PACKET_BITS_ENUM (HEADER_WORD0_PROT_HDR_SZ, 1_WORD),
|
|
|
|
|
IO_PACKET_BITS_VALUE(HEADER_WORD0_PKT_ID, 0),
|
|
|
|
|
IO_PACKET_BITS_VALUE(HEADER_WORD0_CONTROLLER_ID, 0),
|
|
|
|
|
IO_PACKET_BITS_ENUM (HEADER_WORD0_PROTOCOL, I2C),
|
|
|
|
|
IO_PACKET_BITS_ENUM (HEADER_WORD0_PKT_TYPE, REQUEST));
|
|
|
|
|
|
|
|
|
|
/* Enqueue the second header word. */
|
|
|
|
|
reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_VALUE(HEADER_WORD1_PAYLOAD_SIZE, static_cast<u32>(size - 1)));
|
|
|
|
|
reg::Write(m_registers->tx_packet_fifo, IO_PACKET_BITS_VALUE(HEADER_WORD1_PAYLOAD_SIZE, static_cast<u32>(size - 1)));
|
|
|
|
|
|
|
|
|
|
/* Enqueue the protocol header word. */
|
|
|
|
|
reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_HS_MODE, is_hs, ENABLE, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_ON_NACK, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_SEND_START_BYTE, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_READ_WRITE, is_read, READ, WRITE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_ADDRESS_MODE, is_7_bit, SEVEN_BIT, TEN_BIT),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_IE, ENABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_REPEAT_START_STOP, is_stop, STOP_CONDITION, REPEAT_START_CONDITION),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_XFER, USE_REPEAT_START_TOP),
|
|
|
|
|
IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_HS_MASTER_ADDR, 0),
|
|
|
|
|
IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_SLAVE_ADDR, slave_addr));
|
|
|
|
|
reg::Write(m_registers->tx_packet_fifo, IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_HS_MODE, is_hs, ENABLE, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_ON_NACK, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_SEND_START_BYTE, DISABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_READ_WRITE, is_read, READ, WRITE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_ADDRESS_MODE, is_7_bit, SEVEN_BIT, TEN_BIT),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_IE, ENABLE),
|
|
|
|
|
IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_REPEAT_START_STOP, is_stop, STOP_CONDITION, REPEAT_START_CONDITION),
|
|
|
|
|
IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_XFER, USE_REPEAT_START_TOP),
|
|
|
|
|
IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_HS_MASTER_ADDR, 0),
|
|
|
|
|
IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_SLAVE_ADDR, slave_addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::ResetController() const {
|
|
|
|
|
/* Reset the controller. */
|
|
|
|
|
if (!this->is_power_bus) {
|
|
|
|
|
if (!m_is_power_bus) {
|
|
|
|
|
/* Open a clkrst session. */
|
|
|
|
|
clkrst::ClkRstSession clkrst_session;
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code));
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), m_device_code));
|
|
|
|
|
ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); };
|
|
|
|
|
|
|
|
|
|
/* Reset the controller, setting clock rate to 408 MHz / 5 (to account for clock divisor). */
|
|
|
|
|
@@ -573,17 +573,17 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
this->ResetController();
|
|
|
|
|
|
|
|
|
|
/* Configure the sclk threshold for bus clear config. */
|
|
|
|
|
reg::Write(this->registers->bus_clear_config, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9));
|
|
|
|
|
reg::Write(m_registers->bus_clear_config, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9));
|
|
|
|
|
|
|
|
|
|
/* Set stop cond and terminate in bus clear config. */
|
|
|
|
|
reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, STOP));
|
|
|
|
|
reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE));
|
|
|
|
|
reg::ReadWrite(m_registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, STOP));
|
|
|
|
|
reg::ReadWrite(m_registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE));
|
|
|
|
|
|
|
|
|
|
/* Set master config load, busy loop up to 1ms for it to take. */
|
|
|
|
|
reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
|
|
|
|
reg::ReadWrite(m_registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
|
|
|
|
|
|
|
|
|
const os::Tick start_tick_a = os::GetSystemTick();
|
|
|
|
|
while (reg::HasValue(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE))) {
|
|
|
|
|
while (reg::HasValue(m_registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE))) {
|
|
|
|
|
if ((os::GetSystemTick() - start_tick_a).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
|
|
|
|
need_retry = true;
|
|
|
|
|
break;
|
|
|
|
|
@@ -595,10 +595,10 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set bus clear enable, wait up to 1ms for it to take. */
|
|
|
|
|
reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE));
|
|
|
|
|
reg::ReadWrite(m_registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE));
|
|
|
|
|
|
|
|
|
|
const os::Tick start_tick_b = os::GetSystemTick();
|
|
|
|
|
while (reg::HasValue(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE))) {
|
|
|
|
|
while (reg::HasValue(m_registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE))) {
|
|
|
|
|
if ((os::GetSystemTick() - start_tick_b).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
|
|
|
|
need_retry = true;
|
|
|
|
|
break;
|
|
|
|
|
@@ -611,7 +611,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
/* Wait up to 1ms for the bus clear to complete. */
|
|
|
|
|
const os::Tick start_tick_c = os::GetSystemTick();
|
|
|
|
|
while (reg::HasValue(this->registers->bus_clear_status, I2C_REG_BITS_ENUM(BUS_CLEAR_STATUS_BC_STATUS, NOT_CLEARED))) {
|
|
|
|
|
while (reg::HasValue(m_registers->bus_clear_status, I2C_REG_BITS_ENUM(BUS_CLEAR_STATUS_BC_STATUS, NOT_CLEARED))) {
|
|
|
|
|
if ((os::GetSystemTick() - start_tick_c).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
|
|
|
|
need_retry = true;
|
|
|
|
|
break;
|
|
|
|
|
@@ -629,7 +629,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
u32 t_high, t_low, clk_div, debounce, src_div;
|
|
|
|
|
bool high_speed = false;
|
|
|
|
|
|
|
|
|
|
if (this->is_power_bus) {
|
|
|
|
|
if (m_is_power_bus) {
|
|
|
|
|
t_high = 0x02;
|
|
|
|
|
t_low = 0x04;
|
|
|
|
|
clk_div = 0x05;
|
|
|
|
|
@@ -672,26 +672,26 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
/* Write the clock divisors. */
|
|
|
|
|
if (high_speed) {
|
|
|
|
|
reg::Write(this->registers->hs_interface_timing_0, I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_THIGH, t_high),
|
|
|
|
|
I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_TLOW, t_low));
|
|
|
|
|
reg::Write(m_registers->hs_interface_timing_0, I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_THIGH, t_high),
|
|
|
|
|
I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_TLOW, t_low));
|
|
|
|
|
|
|
|
|
|
reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, clk_div));
|
|
|
|
|
reg::Write(m_registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, clk_div));
|
|
|
|
|
} else {
|
|
|
|
|
reg::Write(this->registers->interface_timing_0, I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_THIGH, t_high),
|
|
|
|
|
I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_TLOW, t_low));
|
|
|
|
|
reg::Write(m_registers->interface_timing_0, I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_THIGH, t_high),
|
|
|
|
|
I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_TLOW, t_low));
|
|
|
|
|
|
|
|
|
|
reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, clk_div));
|
|
|
|
|
reg::Write(m_registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, clk_div));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Configure debounce. */
|
|
|
|
|
reg::Write(this->registers->cnfg, I2C_REG_BITS_VALUE(I2C_CNFG_DEBOUNCE_CNT, debounce));
|
|
|
|
|
reg::Read(this->registers->cnfg);
|
|
|
|
|
reg::Write(m_registers->cnfg, I2C_REG_BITS_VALUE(I2C_CNFG_DEBOUNCE_CNT, debounce));
|
|
|
|
|
reg::Read(m_registers->cnfg);
|
|
|
|
|
|
|
|
|
|
/* Set the clock rate, if we should. */
|
|
|
|
|
if (!this->is_power_bus) {
|
|
|
|
|
if (!m_is_power_bus) {
|
|
|
|
|
/* Open a clkrst session. */
|
|
|
|
|
clkrst::ClkRstSession clkrst_session;
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code));
|
|
|
|
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), m_device_code));
|
|
|
|
|
ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); };
|
|
|
|
|
|
|
|
|
|
/* Reset the controller, setting clock rate to 408 MHz / (src_div + 1). */
|
|
|
|
|
@@ -704,26 +704,26 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
void I2cBusAccessor::SetPacketModeRegisters() {
|
|
|
|
|
/* Set packet mode enable. */
|
|
|
|
|
reg::ReadWrite(this->registers->cnfg, I2C_REG_BITS_ENUM(I2C_CNFG_PACKET_MODE_EN, GO));
|
|
|
|
|
reg::ReadWrite(m_registers->cnfg, I2C_REG_BITS_ENUM(I2C_CNFG_PACKET_MODE_EN, GO));
|
|
|
|
|
|
|
|
|
|
/* Set master config load. */
|
|
|
|
|
reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
|
|
|
|
reg::ReadWrite(m_registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
|
|
|
|
|
|
|
|
|
/* Set tx/fifo triggers to default (maximum values). */
|
|
|
|
|
reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7));
|
|
|
|
|
reg::Write(m_registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::FlushFifos() {
|
|
|
|
|
/* Flush the fifo. */
|
|
|
|
|
reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_ENUM (FIFO_CONTROL_RX_FIFO_FLUSH, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM (FIFO_CONTROL_TX_FIFO_FLUSH, SET));
|
|
|
|
|
reg::Write(m_registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7),
|
|
|
|
|
I2C_REG_BITS_ENUM (FIFO_CONTROL_RX_FIFO_FLUSH, SET),
|
|
|
|
|
I2C_REG_BITS_ENUM (FIFO_CONTROL_TX_FIFO_FLUSH, SET));
|
|
|
|
|
|
|
|
|
|
/* Wait up to 5 ms for the flush to complete. */
|
|
|
|
|
int count = 0;
|
|
|
|
|
while (!reg::HasValue(this->registers->fifo_control, I2C_REG_BITS_ENUM(FIFO_CONTROL_FIFO_FLUSH, RX_UNSET_TX_UNSET))) {
|
|
|
|
|
while (!reg::HasValue(m_registers->fifo_control, I2C_REG_BITS_ENUM(FIFO_CONTROL_FIFO_FLUSH, RX_UNSET_TX_UNSET))) {
|
|
|
|
|
R_UNLESS((++count < 5), i2c::ResultBusBusy());
|
|
|
|
|
os::SleepThread(TimeSpan::FromMilliSeconds(1));
|
|
|
|
|
}
|
|
|
|
|
@@ -733,8 +733,8 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
|
|
|
|
|
Result I2cBusAccessor::GetTransactionResult() const {
|
|
|
|
|
/* Get packet status/interrupt status. */
|
|
|
|
|
volatile u32 packet_status = reg::Read(this->registers->packet_transfer_status);
|
|
|
|
|
volatile u32 interrupt_status = reg::Read(this->registers->interrupt_status_register);
|
|
|
|
|
volatile u32 packet_status = reg::Read(m_registers->packet_transfer_status);
|
|
|
|
|
volatile u32 interrupt_status = reg::Read(m_registers->interrupt_status_register);
|
|
|
|
|
|
|
|
|
|
/* Check for ack. */
|
|
|
|
|
R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, UNSET)), i2c::ResultNoAck());
|
|
|
|
|
@@ -759,7 +759,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl {
|
|
|
|
|
this->ResetController();
|
|
|
|
|
|
|
|
|
|
/* Set clock registers. */
|
|
|
|
|
this->SetClockRegisters(this->speed_mode);
|
|
|
|
|
this->SetClockRegisters(m_speed_mode);
|
|
|
|
|
|
|
|
|
|
/* Set packet mode registers. */
|
|
|
|
|
this->SetPacketModeRegisters();
|
|
|
|
|
|