boot: add rgltr/clkrst overrides, skel I2cBusAccessor

This commit is contained in:
Michael Scire 2020-10-31 16:34:06 -07:00 committed by SciresM
parent f4e499fed9
commit e5bf06254a
39 changed files with 1061 additions and 1842 deletions

View file

@ -0,0 +1,245 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "i2c_bus_accessor.hpp"
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);
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;
}
void I2cBusAccessor::RegisterDeviceCode(DeviceCode dc) {
AMS_ASSERT(this->state == State::Initializing);
this->device_code = dc;
}
void I2cBusAccessor::InitializeDriver() {
AMS_ASSERT(this->state == State::Initializing);
this->registers = reinterpret_cast<volatile I2cRegisters *>(dd::QueryIoMapping(this->registers_phys_addr, this->registers_size));
AMS_ABORT_UNLESS(this->registers != nullptr);
this->state = State::Initialized;
}
void I2cBusAccessor::FinalizeDriver() {
AMS_ASSERT(this->state == State::Initialized);
this->state = State::Initializing;
}
Result I2cBusAccessor::InitializeDevice(I2cDeviceProperty *device) {
/* Check that the device is valid. */
AMS_ASSERT(device != nullptr);
AMS_ASSERT(this->state == State::Initialized);
/* Acquire exclusive access. */
std::scoped_lock lk(this->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);
/* Initialize our interrupt event. */
os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear);
os::ClearInterruptEvent(std::addressof(this->interrupt_event);
/* If we're not power bus, perform power management init. */
if (!this->is_power_bus) {
/* Initialize regulator library. */
regulator::Initialize();
/* Try to open regulator session. */
R_TRY(this->TryOpenRegulatorSession());
/* If we have a regulator session, set voltage to 2.9V. */
if (this->has_regulator_session) {
/* NOTE: Nintendo does not check the result, here. */
regulator::SetVoltageValue(std::addressof(this->regulator_session), 2'900'000u);
}
/* Initialize clock/reset library. */
clkrst::Initialize();
}
/* Execute initial config. */
this->ExecuteInitialConfig();
/* If we have a regulator session, enable voltage. */
if (!this->is_power_bus && 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));
}
}
return ResultSuccess();
}
void I2cBusAccessor::FinalizeDevice(I2cDeviceProperty *device) {
/* Check that the device is valid. */
AMS_ASSERT(device != nullptr);
AMS_ASSERT(this->state == State::Initialized);
/* Acquire exclusive access. */
std::scoped_lock lk(this->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;
R_SUCCEED_IF(this->user_count > 0);
/* Finalize our interrupt event. */
os::FinalizeInterruptEvent(std::addressof(this->interrupt_event));
/* If we have a regulator session, disable voltage. */
if (this->has_regulator_session) {
/* NOTE: Nintendo does not check the result of this call. */
regulator::SetVoltageEnabled(std::addressof(this->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;
}
/* Finalize the regulator library. */
regulator::Finalize();
}
Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::SuspendBus() {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::SuspendPowerBus() {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::ResumeBus() {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::ResumePowerBus() {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::TryOpenRegulatorSession() {
/* Ensure we track the session. */
this->has_regulator_session = true;
auto s_guard = SCOPE_GUARD { this->has_regulator_session = false; };
/* Try to open the session. */
R_TRY_CATCH(regulator::OpenSession(std::addressof(this->regulator_session), this->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;
}
} R_END_TRY_CATCH;
/* We opened (or not). */
s_guard.Cancel();
return ResultSuccess();
}
void I2cBusAccessor::ExecuteInitialConfig() {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::ResetController() const {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::ClearBus() const {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::SetPacketModeRegisters() {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::FlushFifos() {
/* TODO */
AMS_ABORT();
}
Result I2cBusAccessor::GetTransactionResult() const {
/* TODO */
AMS_ABORT();
}
void I2cBusAccessor::HandleTransactionError(Result result) {
/* TODO */
AMS_ABORT();
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "i2c_i2c_registers.hpp"
namespace ams::i2c::driver::board::nintendo_nx::impl {
class I2cBusAccessor : public ::ams::i2c::driver::II2cDriver {
NON_COPYABLE(I2cBusAccessor);
NON_MOVEABLE(I2cBusAccessor);
AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::board::nintendo_nx::impl::I2cBusAccessor, ::ams::i2c::driver::II2cDriver);
private:
enum class State {
NotInitialized = 0,
Initializing = 1,
Initialized = 2,
Suspended = 3,
PowerBusSuspended = 4,
};
enum Xfer {
Xfer_Write = 0,
Xfer_Read = 1,
};
private:
volatile I2cRegisters *registers;
SpeedMode speed_mode;
os::InterruptEventType interrupt_event;
int user_count;
os::SdkMutex user_count_mutex;
os::SdkMutex register_mutex;
regulator::RegulatorSession regulator_session;
bool has_regulator_session;
State state;
os::SdkMutex transaction_order_mutex;
bool is_power_bus;
dd::PhysicalAddress registers_phys_addr;
size_t registers_size;
os::InterruptName interrupt_name;
DeviceCode device_code;
util::IntrusiveListNode bus_accessor_list_node;
public:
using BusAccessorListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&I2cBusAccessor::bus_accessor_list_node>;
using BusAccessorList = typename BusAccessorListTraits::ListType;
friend class util::IntrusiveList<I2cBusAccessor, util::IntrusiveListMemberTraitsDeferredAssert<&I2cBusAccessor::bus_accessor_list_node>>;
public:
I2cBusAccessor()
: registers(nullptr), speed_mode(SpeedMode_Fast), user_count(0), user_count_mutex(),
register_mutex(), has_regulator_session(false), state(State::NotInitialized), transaction_order_mutex(),
is_power_bus(false), registers_phys_addr(0), registers_size(0), interrupt_name(), device_code(-1), bus_accessor_list_node()
{
/* ... */
}
void Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm);
void RegisterDeviceCode(DeviceCode device_code);
SpeedMode GetSpeedMode() const { return this->speed_mode; }
dd::PhysicalAddress GetRegistersPhysicalAddress() const { return this->registers_phys_addr; }
size_t GetRegistersSize() const { return this->registers_size; }
os::InterruptName GetInterruptName() const { return this->interrupt_name; }
void RemoveFrom(BusAccessorList &lst) {
lst.erase(lst.iterator_to(*this));
}
bool IsLinkedToList() const {
return this->bus_accessor_list_node.IsLinked();
}
private:
Result TryOpenRegulatorSession();
void ExecuteInitialConfig();
Result Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode);
Result Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode);
void WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode);
void ResetController() const;
void ClearBus() const;
void SetClockRegisters(SpeedMode speed_mode);
void SetPacketModeRegisters();
Result FlushFifos();
Result GetTransactionResult() const;
void HandleTransactionError(Result result);
void DisableInterruptMask() {
reg::Write(this->registers->interrupt_mask_register, 0);
reg::Read(this->registers->interrupt_mask_register);
}
Result CheckAndHandleError() {
const Result result = this->GetTransactionResult();
this->HandleTransactionError(result);
if (R_FAILED(result)) {
this->DisableInterruptMask();
os::ClearInterruptEvent(std::addressof(this->interrupt_event));
return result;
}
return ResultSuccess();
}
public:
virtual void InitializeDriver() override;
virtual void FinalizeDriver() override;
virtual Result InitializeDevice(I2cDeviceProperty *device) override;
virtual void FinalizeDevice(I2cDeviceProperty *device) override;
virtual Result Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) override;
virtual Result Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) override;
virtual os::SdkMutex &GetTransactionOrderMutex() override {
return this->transaction_order_mutex;
}
virtual void SuspendBus() override;
virtual void SuspendPowerBus() override;
virtual void ResumeBus() override;
virtual void ResumePowerBus() override;
virtual const DeviceCode &GetDeviceCode() const override {
return this->device_code;
}
};
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
namespace ams::i2c::driver::board::nintendo_nx::impl {
struct I2cRegisters {
volatile u32 cnfg;
volatile u32 cmd_addr0;
volatile u32 cmd_addr1;
volatile u32 cmd_data1;
volatile u32 cmd_data2;
volatile u32 _14;
volatile u32 _18;
volatile u32 status;
volatile u32 sl_cnfg;
volatile u32 sl_rcvd;
volatile u32 sl_status;
volatile u32 sl_addr1;
volatile u32 sl_addr2;
volatile u32 tlow_sext;
volatile u32 _38;
volatile u32 sl_delay_count;
volatile u32 sl_int_mask;
volatile u32 sl_int_source;
volatile u32 sl_int_set;
volatile u32 _4c;
volatile u32 tx_packet_fifo;
volatile u32 rx_fifo;
volatile u32 packet_transfer_status;
volatile u32 fifo_control;
volatile u32 fifo_status;
volatile u32 interrupt_mask_register;
volatile u32 interrupt_status_register;
volatile u32 clk_divisor_register;
volatile u32 interrupt_source_register;
volatile u32 interrupt_set_register;
volatile u32 slv_tx_packet_fifo;
volatile u32 slv_rx_fifo;
volatile u32 slv_packet_status;
volatile u32 bus_clear_config;
volatile u32 bus_clear_status;
volatile u32 config_load;
volatile u32 _90;
volatile u32 interface_timing_0;
volatile u32 interface_timing_1;
volatile u32 hs_interface_timing_0;
volatile u32 hs_interface_timing_1;
};
static_assert(sizeof(I2cRegisters) == 0xA4);
}