1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-09 21:51:45 +00:00

i2c: implement BusAccessor except Send/Receive/WriteHeader

This commit is contained in:
Michael Scire 2020-10-31 17:58:38 -07:00 committed by SciresM
parent e5bf06254a
commit 6ff58fa4b3
4 changed files with 372 additions and 33 deletions

View file

@ -27,7 +27,7 @@ namespace ams::regulator {
void CloseSession(RegulatorSession *session);
bool GetVoltageEnabled(RegulatorSession *session);
Result SetVoltageEnabled(RegulatorSession *session);
Result SetVoltageEnabled(RegulatorSession *session, bool enabled);
Result SetVoltageValue(RegulatorSession *session, u32 micro_volts);

View file

@ -64,7 +64,7 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
/* 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::ClearInterruptEvent(std::addressof(this->interrupt_event));
/* If we're not power bus, perform power management init. */
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. */
AMS_ASSERT(this->user_count > 0);
--this->user_count;
R_SUCCEED_IF(this->user_count > 0);
if (this->user_count > 0) {
return;
}
/* Finalize our 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) {
/* TODO */
AMS_ABORT();
/* Check pre-conditions. */
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) {
/* TODO */
AMS_ABORT();
/* Check pre-conditions. */
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() {
/* TODO */
AMS_ABORT();
/* Check that state is valid. */
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() {
/* TODO */
AMS_ABORT();
/* Check that state is valid. */
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() {
/* TODO */
AMS_ABORT();
/* Check that state is valid. */
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() {
/* TODO */
AMS_ABORT();
/* Check that state is valid. */
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() {
@ -188,8 +286,20 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
}
void I2cBusAccessor::ExecuteInitialConfig() {
/* TODO */
AMS_ABORT();
/* Lock exclusive access to registers. */
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) {
@ -208,38 +318,231 @@ namespace ams::i2c::driver::board::nintendo_nx::impl {
}
void I2cBusAccessor::ResetController() const {
/* TODO */
AMS_ABORT();
/* Reset the controller. */
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 {
/* TODO */
AMS_ABORT();
/* Try to clear the bus up to three times. */
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) {
/* TODO */
AMS_ABORT();
/* Determine parameters for the speed mode. */
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() {
/* TODO */
AMS_ABORT();
/* Set packet mode enable. */
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() {
/* TODO */
AMS_ABORT();
/* 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));
/* 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 {
/* TODO */
AMS_ABORT();
/* 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);
/* 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) {
/* TODO */
AMS_ABORT();
R_TRY_CATCH(result) {
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;
}
}

View file

@ -25,11 +25,17 @@
#define I2C_I2C_CMD_ADDR0 (0x004)
#define I2C_I2C_CMD_DATA1 (0x00C)
#define I2C_I2C_STATUS (0x01C)
#define I2C_PACKET_TRANSFER_STATUS (0x058)
#define I2C_FIFO_CONTROL (0x05C)
#define I2C_INTERRUPT_STATUS_REGISTER (0x068)
#define I2C_CLK_DIVISOR_REGISTER (0x06C)
#define I2C_BUS_CLEAR_CONFIG (0x084)
#define I2C_BUS_CLEAR_STATUS (0x088)
#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_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_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_PACKET_MODE_EN, 9, NOP, GO);
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);
@ -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_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 */
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);
/* 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(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 */
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_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE);
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);

View file

@ -39,8 +39,8 @@ namespace ams::regulator {
return true;
}
Result SetVoltageEnabled(RegulatorSession *session) {
AMS_UNUSED(session);
Result SetVoltageEnabled(RegulatorSession *session, bool enabled) {
AMS_UNUSED(session, enabled);
return ResultSuccess();
}