mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-14 00:56:35 +00:00
i2c: implement BusAccessor except Send/Receive/WriteHeader
This commit is contained in:
parent
e5bf06254a
commit
6ff58fa4b3
4 changed files with 372 additions and 33 deletions
|
@ -27,7 +27,7 @@ namespace ams::regulator {
|
||||||
void CloseSession(RegulatorSession *session);
|
void CloseSession(RegulatorSession *session);
|
||||||
|
|
||||||
bool GetVoltageEnabled(RegulatorSession *session);
|
bool GetVoltageEnabled(RegulatorSession *session);
|
||||||
Result SetVoltageEnabled(RegulatorSession *session);
|
Result SetVoltageEnabled(RegulatorSession *session, bool enabled);
|
||||||
|
|
||||||
Result SetVoltageValue(RegulatorSession *session, u32 micro_volts);
|
Result SetVoltageValue(RegulatorSession *session, u32 micro_volts);
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
|
||||||
|
|
||||||
/* Initialize our interrupt event. */
|
/* Initialize our interrupt event. */
|
||||||
os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear);
|
os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear);
|
||||||
os::ClearInterruptEvent(std::addressof(this->interrupt_event);
|
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
|
||||||
|
|
||||||
/* If we're not power bus, perform power management init. */
|
/* If we're not power bus, perform power management init. */
|
||||||
if (!this->is_power_bus) {
|
if (!this->is_power_bus) {
|
||||||
|
@ -115,7 +115,9 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
|
||||||
/* Increment our user count -- if we're not the last user, we're done. */
|
/* Increment our user count -- if we're not the last user, we're done. */
|
||||||
AMS_ASSERT(this->user_count > 0);
|
AMS_ASSERT(this->user_count > 0);
|
||||||
--this->user_count;
|
--this->user_count;
|
||||||
R_SUCCEED_IF(this->user_count > 0);
|
if (this->user_count > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Finalize our interrupt event. */
|
/* Finalize our interrupt event. */
|
||||||
os::FinalizeInterruptEvent(std::addressof(this->interrupt_event));
|
os::FinalizeInterruptEvent(std::addressof(this->interrupt_event));
|
||||||
|
@ -140,33 +142,129 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) {
|
Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) {
|
||||||
/* TODO */
|
/* Check pre-conditions. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(device != nullptr);
|
||||||
|
AMS_ASSERT(src != nullptr);
|
||||||
|
AMS_ASSERT(src_size > 0);
|
||||||
|
|
||||||
|
if (this->is_power_bus) {
|
||||||
|
AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended);
|
||||||
|
} else {
|
||||||
|
AMS_ASSERT(this->state == State::Initialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the data. */
|
||||||
|
return this->Send(static_cast<const u8 *>(src), src_size, option, device->GetAddress(), device->GetAddressingMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) {
|
Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) {
|
||||||
/* TODO */
|
/* Check pre-conditions. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(device != nullptr);
|
||||||
|
AMS_ASSERT(dst != nullptr);
|
||||||
|
AMS_ASSERT(dst_size > 0);
|
||||||
|
|
||||||
|
if (this->is_power_bus) {
|
||||||
|
AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended);
|
||||||
|
} else {
|
||||||
|
AMS_ASSERT(this->state == State::Initialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the data. */
|
||||||
|
return this->Receive(static_cast<u8 *>(dst), dst_size, option, device->GetAddress(), device->GetAddressingMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::SuspendBus() {
|
void I2cBusAccessor::SuspendBus() {
|
||||||
/* TODO */
|
/* Check that state is valid. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(this->state == State::Initialized);
|
||||||
|
|
||||||
|
/* Acquire exclusive access. */
|
||||||
|
std::scoped_lock lk(this->user_count_mutex);
|
||||||
|
|
||||||
|
/* If we need to, disable clock/voltage appropriately. */
|
||||||
|
if (!this->is_power_bus && this->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));
|
||||||
|
ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); };
|
||||||
|
|
||||||
|
/* Set clock disabled for the session. */
|
||||||
|
clkrst::SetClockDisabled(std::addressof(clkrst_session));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable voltage. */
|
||||||
|
if (this->has_regulator_session) {
|
||||||
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update state. */
|
||||||
|
this->state = State::Suspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::SuspendPowerBus() {
|
void I2cBusAccessor::SuspendPowerBus() {
|
||||||
/* TODO */
|
/* Check that state is valid. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(this->state == State::Suspended);
|
||||||
|
|
||||||
|
/* Acquire exclusive access. */
|
||||||
|
std::scoped_lock lk(this->user_count_mutex);
|
||||||
|
|
||||||
|
/* If we need to, disable clock/voltage appropriately. */
|
||||||
|
if (this->is_power_bus && this->user_count > 0) {
|
||||||
|
/* Nothing should actually be done here. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update state. */
|
||||||
|
this->state = State::PowerBusSuspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::ResumeBus() {
|
void I2cBusAccessor::ResumeBus() {
|
||||||
/* TODO */
|
/* Check that state is valid. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(this->state == State::Suspended);
|
||||||
|
|
||||||
|
/* Acquire exclusive access. */
|
||||||
|
std::scoped_lock lk(this->user_count_mutex);
|
||||||
|
|
||||||
|
/* If we need to, enable clock/voltage appropriately. */
|
||||||
|
if (!this->is_power_bus && this->user_count > 0) {
|
||||||
|
/* Enable voltage. */
|
||||||
|
if (this->has_regulator_session) {
|
||||||
|
/* Check whether voltage was already enabled. */
|
||||||
|
const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session));
|
||||||
|
|
||||||
|
/* NOTE: Nintendo does not check the result of this call. */
|
||||||
|
regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true);
|
||||||
|
|
||||||
|
/* If we enabled voltage, delay to give our enable time to take. */
|
||||||
|
if (!was_enabled) {
|
||||||
|
os::SleepThread(TimeSpan::FromMicroSeconds(560));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute initial config, which will enable clock as relevant. */
|
||||||
|
this->ExecuteInitialConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update state. */
|
||||||
|
this->state = State::Initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::ResumePowerBus() {
|
void I2cBusAccessor::ResumePowerBus() {
|
||||||
/* TODO */
|
/* Check that state is valid. */
|
||||||
AMS_ABORT();
|
AMS_ASSERT(this->state == State::PowerBusSuspended);
|
||||||
|
|
||||||
|
/* Acquire exclusive access. */
|
||||||
|
std::scoped_lock lk(this->user_count_mutex);
|
||||||
|
|
||||||
|
/* If we need to, enable clock/voltage appropriately. */
|
||||||
|
if (this->is_power_bus && this->user_count > 0) {
|
||||||
|
/* Execute initial config, which will enable clock as relevant. */
|
||||||
|
this->ExecuteInitialConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update state. */
|
||||||
|
this->state = State::Suspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::TryOpenRegulatorSession() {
|
Result I2cBusAccessor::TryOpenRegulatorSession() {
|
||||||
|
@ -188,8 +286,20 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::ExecuteInitialConfig() {
|
void I2cBusAccessor::ExecuteInitialConfig() {
|
||||||
/* TODO */
|
/* Lock exclusive access to registers. */
|
||||||
AMS_ABORT();
|
std::scoped_lock lk(this->register_mutex);
|
||||||
|
|
||||||
|
/* Reset the controller. */
|
||||||
|
this->ResetController();
|
||||||
|
|
||||||
|
/* Set clock registers. */
|
||||||
|
this->SetClockRegisters(this->speed_mode);
|
||||||
|
|
||||||
|
/* Set packet mode registers. */
|
||||||
|
this->SetPacketModeRegisters();
|
||||||
|
|
||||||
|
/* Flush fifos. */
|
||||||
|
this->FlushFifos();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
|
Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
|
||||||
|
@ -208,38 +318,231 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::ResetController() const {
|
void I2cBusAccessor::ResetController() const {
|
||||||
/* TODO */
|
/* Reset the controller. */
|
||||||
AMS_ABORT();
|
if (!this->is_power_bus) {
|
||||||
|
/* Open a clkrst session. */
|
||||||
|
clkrst::ClkRstSession clkrst_session;
|
||||||
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->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). */
|
||||||
|
/* NOTE: Nintendo does not check result for any of these calls. */
|
||||||
|
clkrst::SetResetAsserted(std::addressof(clkrst_session));
|
||||||
|
clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (4 + 1));
|
||||||
|
clkrst::SetResetDeasserted(std::addressof(clkrst_session));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::ClearBus() const {
|
void I2cBusAccessor::ClearBus() const {
|
||||||
/* TODO */
|
/* Try to clear the bus up to three times. */
|
||||||
AMS_ABORT();
|
constexpr int MaxRetryCount = 3;
|
||||||
|
constexpr int BusyLoopMicroSeconds = 1000;
|
||||||
|
|
||||||
|
int try_count = 0;
|
||||||
|
bool need_retry;
|
||||||
|
do {
|
||||||
|
/* Update trackers. */
|
||||||
|
++try_count;
|
||||||
|
need_retry = false;
|
||||||
|
|
||||||
|
/* Reset the controller. */
|
||||||
|
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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
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))) {
|
||||||
|
if ((os::GetSystemTick() - start_tick_a).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
||||||
|
need_retry = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_retry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
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))) {
|
||||||
|
if ((os::GetSystemTick() - start_tick_b).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
||||||
|
need_retry = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_retry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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))) {
|
||||||
|
if ((os::GetSystemTick() - start_tick_c).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) {
|
||||||
|
need_retry = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_retry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (try_count < MaxRetryCount && need_retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) {
|
void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) {
|
||||||
/* TODO */
|
/* Determine parameters for the speed mode. */
|
||||||
AMS_ABORT();
|
u32 t_high, t_low, clk_div, debounce, src_div;
|
||||||
|
bool high_speed = false;
|
||||||
|
|
||||||
|
if (this->is_power_bus) {
|
||||||
|
t_high = 0x02;
|
||||||
|
t_low = 0x04;
|
||||||
|
clk_div = 0x05;
|
||||||
|
debounce = 0x02;
|
||||||
|
} else {
|
||||||
|
switch (speed_mode) {
|
||||||
|
case SpeedMode_Standard:
|
||||||
|
t_high = 0x02;
|
||||||
|
t_low = 0x04;
|
||||||
|
clk_div = 0x19;
|
||||||
|
debounce = 0x02;
|
||||||
|
src_div = 0x13;
|
||||||
|
break;
|
||||||
|
case SpeedMode_Fast:
|
||||||
|
t_high = 0x02;
|
||||||
|
t_low = 0x04;
|
||||||
|
clk_div = 0x19;
|
||||||
|
debounce = 0x02;
|
||||||
|
src_div = 0x04;
|
||||||
|
break;
|
||||||
|
case SpeedMode_FastPlus:
|
||||||
|
t_high = 0x02;
|
||||||
|
t_low = 0x04;
|
||||||
|
clk_div = 0x10;
|
||||||
|
debounce = 0x00;
|
||||||
|
src_div = 0x02;
|
||||||
|
break;
|
||||||
|
case SpeedMode_HighSpeed:
|
||||||
|
t_high = 0x03;
|
||||||
|
t_low = 0x08;
|
||||||
|
clk_div = 0x02;
|
||||||
|
debounce = 0x00;
|
||||||
|
src_div = 0x02;
|
||||||
|
high_speed = true;
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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(this->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(this->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);
|
||||||
|
|
||||||
|
/* Set the clock rate, if we should. */
|
||||||
|
if (!this->is_power_bus) {
|
||||||
|
/* Open a clkrst session. */
|
||||||
|
clkrst::ClkRstSession clkrst_session;
|
||||||
|
R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code));
|
||||||
|
ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); };
|
||||||
|
|
||||||
|
/* Reset the controller, setting clock rate to 408 MHz / (src_div + 1). */
|
||||||
|
/* NOTE: Nintendo does not check result for any of these calls. */
|
||||||
|
clkrst::SetResetAsserted(std::addressof(clkrst_session));
|
||||||
|
clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (src_div + 1));
|
||||||
|
clkrst::SetResetDeasserted(std::addressof(clkrst_session));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::SetPacketModeRegisters() {
|
void I2cBusAccessor::SetPacketModeRegisters() {
|
||||||
/* TODO */
|
/* Set packet mode enable. */
|
||||||
AMS_ABORT();
|
reg::ReadWrite(this->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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::FlushFifos() {
|
Result I2cBusAccessor::FlushFifos() {
|
||||||
/* TODO */
|
/* Flush the fifo. */
|
||||||
AMS_ABORT();
|
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));
|
||||||
|
|
||||||
|
/* 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))) {
|
||||||
|
R_UNLESS((++count < 5), i2c::ResultBusBusy());
|
||||||
|
os::SleepThread(TimeSpan::FromMilliSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cBusAccessor::GetTransactionResult() const {
|
Result I2cBusAccessor::GetTransactionResult() const {
|
||||||
/* TODO */
|
/* Get packet status/interrupt status. */
|
||||||
AMS_ABORT();
|
volatile u32 packet_status = reg::Read(this->registers->packet_transfer_status);
|
||||||
|
volatile u32 interrupt_status = reg::Read(this->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());
|
||||||
|
R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, UNSET)), i2c::ResultNoAck());
|
||||||
|
R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, UNSET)), i2c::ResultNoAck());
|
||||||
|
|
||||||
|
/* If we lost arbitration, we'll need to clear the bus. */
|
||||||
|
auto clear_guard = SCOPE_GUARD { this->ClearBus(); };
|
||||||
|
|
||||||
|
/* Check for arb lost. */
|
||||||
|
R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, UNSET)), i2c::ResultBusBusy());
|
||||||
|
R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, UNSET)), i2c::ResultBusBusy());
|
||||||
|
|
||||||
|
clear_guard.Cancel();
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2cBusAccessor::HandleTransactionError(Result result) {
|
void I2cBusAccessor::HandleTransactionError(Result result) {
|
||||||
/* TODO */
|
R_TRY_CATCH(result) {
|
||||||
AMS_ABORT();
|
R_CATCH(i2c::ResultNoAck, i2c::ResultBusBusy) {
|
||||||
|
/* Reset the controller. */
|
||||||
|
this->ResetController();
|
||||||
|
|
||||||
|
/* Set clock registers. */
|
||||||
|
this->SetClockRegisters(this->speed_mode);
|
||||||
|
|
||||||
|
/* Set packet mode registers. */
|
||||||
|
this->SetPacketModeRegisters();
|
||||||
|
|
||||||
|
/* Flush fifos. */
|
||||||
|
this->FlushFifos();
|
||||||
|
}
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,17 @@
|
||||||
#define I2C_I2C_CMD_ADDR0 (0x004)
|
#define I2C_I2C_CMD_ADDR0 (0x004)
|
||||||
#define I2C_I2C_CMD_DATA1 (0x00C)
|
#define I2C_I2C_CMD_DATA1 (0x00C)
|
||||||
#define I2C_I2C_STATUS (0x01C)
|
#define I2C_I2C_STATUS (0x01C)
|
||||||
|
#define I2C_PACKET_TRANSFER_STATUS (0x058)
|
||||||
|
#define I2C_FIFO_CONTROL (0x05C)
|
||||||
#define I2C_INTERRUPT_STATUS_REGISTER (0x068)
|
#define I2C_INTERRUPT_STATUS_REGISTER (0x068)
|
||||||
#define I2C_CLK_DIVISOR_REGISTER (0x06C)
|
#define I2C_CLK_DIVISOR_REGISTER (0x06C)
|
||||||
#define I2C_BUS_CLEAR_CONFIG (0x084)
|
#define I2C_BUS_CLEAR_CONFIG (0x084)
|
||||||
#define I2C_BUS_CLEAR_STATUS (0x088)
|
#define I2C_BUS_CLEAR_STATUS (0x088)
|
||||||
#define I2C_CONFIG_LOAD (0x08C)
|
#define I2C_CONFIG_LOAD (0x08C)
|
||||||
|
#define I2C_INTERFACE_TIMING_0 (0x094)
|
||||||
|
#define I2C_INTERFACE_TIMING_1 (0x098)
|
||||||
|
#define I2C_HS_INTERFACE_TIMING_0 (0x094)
|
||||||
|
#define I2C_HS_INTERFACE_TIMING_1 (0x098)
|
||||||
|
|
||||||
#define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME)
|
#define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME)
|
||||||
#define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE)
|
#define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE)
|
||||||
|
@ -46,6 +52,7 @@
|
||||||
DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3);
|
DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ);
|
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO);
|
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_PACKET_MODE_EN, 9, NOP, GO);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE);
|
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE);
|
||||||
DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T);
|
DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T);
|
||||||
|
|
||||||
|
@ -58,7 +65,24 @@ DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_N
|
||||||
DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15);
|
DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY);
|
DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY);
|
||||||
|
|
||||||
|
/* PACKET_TRANSFER_STATUS */
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_CONTROLLER_BUSY, 0, UNSET, SET);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, 1, UNSET, SET);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, 2, UNSET, SET);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, 3, UNSET, SET);
|
||||||
|
|
||||||
|
/* FIFO_CONTROL */
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(FIFO_CONTROL_RX_FIFO_FLUSH, 0, UNSET, SET);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(FIFO_CONTROL_TX_FIFO_FLUSH, 1, UNSET, SET);
|
||||||
|
|
||||||
|
DEFINE_I2C_REG_TWO_BIT_ENUM(FIFO_CONTROL_FIFO_FLUSH, 0, RX_UNSET_TX_UNSET, RX_SET_TX_UNSET, RX_UNSET_TX_SET, RX_SET_TX_SET);
|
||||||
|
|
||||||
|
DEFINE_I2C_REG(FIFO_CONTROL_RX_FIFO_TRIG, 2, 3);
|
||||||
|
DEFINE_I2C_REG(FIFO_CONTROL_TX_FIFO_TRIG, 5, 3);
|
||||||
|
|
||||||
/* INTERRUPT_STATUS_REGISTER */
|
/* INTERRUPT_STATUS_REGISTER */
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, 2, UNSET, SET);
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, 3, UNSET, SET);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET);
|
DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET);
|
||||||
|
|
||||||
/* CLK_DIVISOR_REGISTER */
|
/* CLK_DIVISOR_REGISTER */
|
||||||
|
@ -71,8 +95,20 @@ DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP);
|
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP);
|
||||||
DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8);
|
DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8);
|
||||||
|
|
||||||
|
/* BUS_CLEAR_STATUS */
|
||||||
|
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_STATUS_BC_STATUS, 0, NOT_CLEARED, CLEARED);
|
||||||
|
|
||||||
/* CONFIG_LOAD */
|
/* CONFIG_LOAD */
|
||||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE);
|
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE);
|
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE);
|
||||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE);
|
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE);
|
||||||
DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1);
|
DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1);
|
||||||
|
|
||||||
|
/* INTERFACE_TIMING_0 */
|
||||||
|
DEFINE_I2C_REG(INTERFACE_TIMING_0_TLOW, 0, 6);
|
||||||
|
DEFINE_I2C_REG(INTERFACE_TIMING_0_THIGH, 8, 6);
|
||||||
|
|
||||||
|
/* HS_INTERFACE_TIMING_0 */
|
||||||
|
DEFINE_I2C_REG(HS_INTERFACE_TIMING_0_HS_TLOW, 0, 6);
|
||||||
|
DEFINE_I2C_REG(HS_INTERFACE_TIMING_0_HS_THIGH, 8, 6);
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ namespace ams::regulator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetVoltageEnabled(RegulatorSession *session) {
|
Result SetVoltageEnabled(RegulatorSession *session, bool enabled) {
|
||||||
AMS_UNUSED(session);
|
AMS_UNUSED(session, enabled);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue