From 09f3b29a9826da6a8e6c022b035a7039d2ee0a8c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 31 Oct 2020 19:14:43 -0700 Subject: [PATCH] i2c: finish I2cBusAccessor --- .../nintendo_nx/impl/i2c_bus_accessor.cpp | 170 +++++++++++++++++- .../include/vapours/results/i2c_results.hpp | 4 +- .../include/vapours/tegra/tegra_i2c.hpp | 29 ++- 3 files changed, 194 insertions(+), 9 deletions(-) diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp index d6866bb52..5287d9101 100644 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp @@ -20,6 +20,8 @@ namespace ams::i2c::driver::board::nintendo_nx::impl { namespace { + constexpr inline TimeSpan Timeout = TimeSpan::FromMilliSeconds(100); + #define IO_PACKET_BITS_MASK(NAME) REG_NAMED_BITS_MASK (_IMPL_IO_PACKET_, NAME) #define IO_PACKET_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (_IMPL_IO_PACKET_, NAME, VALUE) #define IO_PACKET_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (_IMPL_IO_PACKET_, NAME, ENUM) @@ -37,7 +39,7 @@ namespace ams::i2c::driver::board::nintendo_nx::impl { DEFINE_IO_PACKET_REG(HEADER_WORD0_PKT_ID, 16, 8); DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(HEADER_WORD0_PROT_HDR_SZ, 28, 1_WORD, 2_WORD, 3_WORD, 4_WORD); - DEFINE_IO_PACKET_REG(HEADER_WORD1_PAYLOAD_SIZE, 0, 11); + DEFINE_IO_PACKET_REG(HEADER_WORD1_PAYLOAD_SIZE, 0, 12); DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_SLAVE_ADDR, 0, 10); DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_HS_MASTER_ADDR, 12, 3); @@ -337,13 +339,170 @@ 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) { - /* TODO */ - AMS_ABORT(); + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->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(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)); + + /* Write the header. */ + this->WriteHeader(Xfer_Write, src_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + const u8 *cur = src; + size_t remaining = src_size; + + 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)); + + /* Write up to (empty) bytes to the fifo. */ + for (u32 i = 0; remaining > 0 && i < empty; ++i) { + /* Build the data word to send. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + + u32 word = 0; + for (size_t j = 0; j < cur_bytes; ++j) { + word |= cur[j] << (BITSIZEOF(u8) * j); + } + + /* Write the data word. */ + reg::Write(this->registers->tx_packet_fifo, word); + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + + /* If we're done, break. */ + if (remaining == 0) { + break; + } + + /* Wait for our current data to send. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultInterruptTimeout(); + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + } + + /* 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)); + + /* Wait for the packet transfer to complete. */ + while (true) { + /* Check and handle any errors. */ + 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))) { + break; + } + + /* Wait for our the packet to transfer. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultInterruptTimeout(); + } + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* We're done. */ + this->DisableInterruptMask(); + return ResultSuccess(); } Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { - /* TODO */ - AMS_ABORT(); + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->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(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)); + + /* Write the header. */ + this->WriteHeader(Xfer_Read, dst_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + u8 *cur = dst; + size_t remaining = dst_size; + + 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)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultInterruptTimeout(); + } + + /* Check and handle any errors. */ + 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)); + + /* Determine how many words we can read. */ + const size_t cur_words = std::min(util::DivideUp(remaining, sizeof(u32)), static_cast(full)); + + /* 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); + + /* Copy bytes from the word. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + for (size_t j = 0; j < cur_bytes; ++j) { + cur[j] = (word >> (BITSIZEOF(u8) * j)) & 0xFF; + } + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + } + + /* We're done. */ + return ResultSuccess(); } void I2cBusAccessor::WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { @@ -473,6 +632,7 @@ namespace ams::i2c::driver::board::nintendo_nx::impl { t_low = 0x04; clk_div = 0x05; debounce = 0x02; + src_div = 0; /* unused */ } else { switch (speed_mode) { case SpeedMode_Standard: diff --git a/libraries/libvapours/include/vapours/results/i2c_results.hpp b/libraries/libvapours/include/vapours/results/i2c_results.hpp index 62cb26eb7..6204723ee 100644 --- a/libraries/libvapours/include/vapours/results/i2c_results.hpp +++ b/libraries/libvapours/include/vapours/results/i2c_results.hpp @@ -24,7 +24,9 @@ namespace ams::i2c { R_DEFINE_ERROR_RESULT(NoAck, 1); R_DEFINE_ERROR_RESULT(BusBusy, 2); R_DEFINE_ERROR_RESULT(FullCommandList, 3); - R_DEFINE_ERROR_RESULT(TimedOut, 4); + R_DEFINE_ERROR_RESULT(UnknownDevice, 5); + R_DEFINE_ERROR_RESULT(InterruptTimeout, 253); + } diff --git a/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp b/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp index 23cf5ba12..5e09f7928 100644 --- a/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp @@ -27,6 +27,8 @@ #define I2C_I2C_STATUS (0x01C) #define I2C_PACKET_TRANSFER_STATUS (0x058) #define I2C_FIFO_CONTROL (0x05C) +#define I2C_FIFO_STATUS (0x060) +#define I2C_INTERRUPT_MASK_REGISTER (0x064) #define I2C_INTERRUPT_STATUS_REGISTER (0x068) #define I2C_CLK_DIVISOR_REGISTER (0x06C) #define I2C_BUS_CLEAR_CONFIG (0x084) @@ -80,10 +82,31 @@ DEFINE_I2C_REG_TWO_BIT_ENUM(FIFO_CONTROL_FIFO_FLUSH, 0, RX_UNSET_TX_UNSET, RX_SE DEFINE_I2C_REG(FIFO_CONTROL_RX_FIFO_TRIG, 2, 3); DEFINE_I2C_REG(FIFO_CONTROL_TX_FIFO_TRIG, 5, 3); +/* FIFO_STATUS */ +DEFINE_I2C_REG(FIFO_STATUS_RX_FIFO_FULL_CNT, 0, 4); +DEFINE_I2C_REG(FIFO_STATUS_TX_FIFO_EMPTY_CNT, 4, 4); + +/* INTERRUPT_MASK_REGISTER */ +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, 0, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, 1, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, 2, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, 3, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_UNF_INT_EN, 4, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_OVF_INT_EN, 5, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_ALL_PACKETS_XFER_COMPLETE_INT_EN, 6, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, 7, DISABLE, ENABLE); + /* 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_RFIFO_DATA_REQ, 0, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_DATA_REQ, 1, UNSET, SET); +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_RFIFO_UNF, 4, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, 5, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, 6, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, 7, UNSET, SET); + +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET); /* CLK_DIVISOR_REGISTER */ DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16);