Files
IronOS/source/Core/BSP/Pinecilv2/I2C_Wrapper.cpp
Ben V. Brown c6f5fdd7be
Some checks are pending
CI / build (MHP30) (push) Waiting to run
CI / tests (push) Waiting to run
CI / build (Pinecil) (push) Waiting to run
CI / build (Pinecilv2) (push) Waiting to run
CI / build (S60) (push) Waiting to run
CI / build (S60P) (push) Waiting to run
CI / build (T55) (push) Waiting to run
CI / build (TS100) (push) Waiting to run
CI / check_c-cpp (push) Waiting to run
CI / build (TS101) (push) Waiting to run
CI / build (TS80) (push) Waiting to run
CI / build (TS80P) (push) Waiting to run
CI / build_multi-lang (Pinecil) (push) Waiting to run
CI / build_multi-lang (Pinecilv2) (push) Waiting to run
CI / upload_metadata (push) Blocked by required conditions
CI / check_python (push) Waiting to run
CI / check_shell (push) Waiting to run
CI / check_docs (push) Waiting to run
Refactor I2C result to use notifications, allowing faster task yield
So that one I2C done, task can directly swap and pickup later if I2C is blocking
2025-02-22 17:33:33 +11:00

272 lines
8.6 KiB
C++

/*
* FRToSI2C.cpp
*
* Created on: 14Apr.,2018
* Author: Ralim
*/
#include "BSP.h"
#include "IRQ.h"
#include "Setup.h"
#include "bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_dma.h"
extern "C" {
#include "bflb_platform.h"
#include "bl702_dma.h"
#include "bl702_glb.h"
#include "bl702_i2c.h"
}
#include <I2C_Wrapper.hpp>
// Semaphore for locking users of I2C
SemaphoreHandle_t FRToSI2C::I2CSemaphore = nullptr;
StaticSemaphore_t FRToSI2C::xSemaphoreBuffer;
#define I2C_TIME_OUT (uint16_t)(12000)
#define I2C_TX_FIFO_ADDR (0x4000A300 + 0x88)
#define I2C_RX_FIFO_ADDR (0x4000A300 + 0x8C)
// Used by the irq handler
volatile uint8_t *IRQDataPointer;
volatile uint8_t IRQDataSizeLeft;
volatile bool IRQFailureMarker;
volatile TaskHandle_t IRQTaskWaitingHandle = NULL;
/****** IRQ Handlers ******/
void i2c_irq_tx_fifo_low() {
// Filling tx fifo
// Fifo is 32 bit, LSB sent first
// FiFo can store up to 2, 32-bit words
// So we fill it until it has no free room (or we run out of data)
while (IRQDataSizeLeft > 0 && I2C_GetTXFIFOAvailable() > 0) {
// Can put in at least 1 byte
// Build a 32-bit word from bytes
uint32_t value = 0;
int packing = IRQDataSizeLeft >= 4 ? 0 : 4 - IRQDataSizeLeft;
for (int i = 0; i < 4 && IRQDataSizeLeft > 0; i++) {
value >>= 8;
value |= (*IRQDataPointer) << 24; // Shift to the left, adding new data to the higher byte
IRQDataPointer++; // Shift to next byte
IRQDataSizeLeft--;
}
// Handle shunting remaining bytes if not a full 4 to send
for (int i = 0; i < packing; i++) {
value >>= 8;
}
// Push the new value to the fifo
*((volatile uint32_t *)I2C_TX_FIFO_ADDR) = value;
}
if (IRQDataSizeLeft == 0) {
// Disable IRQ, were done
I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, MASK);
}
}
void i2c_rx_pop_fifo() {
// Pop one word from the fifo and store it
uint32_t value = *((uint32_t *)I2C_RX_FIFO_ADDR);
for (int i = 0; i < 4 && IRQDataSizeLeft > 0; i++) {
*IRQDataPointer = value & 0xFF;
IRQDataPointer++;
IRQDataSizeLeft--;
value >>= 8;
}
}
void i2c_irq_rx_fifo_ready() {
// Draining the Rx FiFo
while (I2C_GetRXFIFOAvailable() > 0) {
i2c_rx_pop_fifo();
}
if (IRQDataSizeLeft == 0) {
// Disable IRQ, were done
I2C_IntMask(I2C0_ID, I2C_RX_FIFO_READY_INT, MASK);
}
}
void i2c_irq_done_read() {
IRQFailureMarker = false;
// If there was a non multiple of 4 bytes to be read, they are pushed to the fifo now (end of transfer interrupt)
// So we catch them here
while (I2C_GetRXFIFOAvailable() > 0) {
i2c_rx_pop_fifo();
}
// Mask IRQ's back off
FRToSI2C::CpltCallback(); // Causes the lock to be released
}
void i2c_irq_done() {
IRQFailureMarker = false;
// Mask IRQ's back off
FRToSI2C::CpltCallback(); // Causes the lock to be released
}
void i2c_irq_nack() {
IRQFailureMarker = true;
// Mask IRQ's back off
FRToSI2C::CpltCallback(); // Causes the lock to be released
}
/****** END IRQ Handlers ******/
void FRToSI2C::CpltCallback() {
// This is only triggered from IRQ context
I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, MASK);
I2C_IntMask(I2C0_ID, I2C_RX_FIFO_READY_INT, MASK);
I2C_IntMask(I2C0_ID, I2C_TRANS_END_INT, MASK);
I2C_IntMask(I2C0_ID, I2C_NACK_RECV_INT, MASK);
CPU_Interrupt_Disable(I2C_IRQn); // Disable IRQ's
I2C_Disable(I2C0_ID); // Disable I2C to tidy up
// Unlock the semaphore && allow task switch if desired by RTOS
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(I2CSemaphore, &xHigherPriorityTaskWoken);
xTaskNotifyFromISR(IRQTaskWaitingHandle, IRQFailureMarker ? 2 : 1, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
bool FRToSI2C::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) { return Mem_Write(address, reg, &data, 1); }
uint8_t FRToSI2C::I2C_RegisterRead(uint8_t add, uint8_t reg) {
uint8_t temp = 0;
Mem_Read(add, reg, &temp, 1);
return temp;
}
bool FRToSI2C::Mem_Read(uint16_t DevAddress, uint16_t read_address, uint8_t *p_buffer, uint16_t number_of_byte) {
if (!lock()) {
return false;
}
I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0};
BL_Err_Type err = ERROR;
i2cCfg.slaveAddr = DevAddress >> 1;
i2cCfg.stopEveryByte = DISABLE;
i2cCfg.subAddr = read_address;
i2cCfg.dataSize = number_of_byte;
i2cCfg.data = p_buffer;
i2cCfg.subAddrSize = 1; // one byte address
// Store handles for IRQ
IRQDataPointer = p_buffer;
IRQDataSizeLeft = number_of_byte;
IRQTaskWaitingHandle = xTaskGetCurrentTaskHandle();
IRQFailureMarker = false;
I2C_Disable(I2C0_ID);
// Setup and run
I2C_Init(I2C0_ID, I2C_READ, &i2cCfg); // Setup hardware for the I2C init header with the device address
I2C_IntMask(I2C0_ID, I2C_TRANS_END_INT, UNMASK);
I2C_IntMask(I2C0_ID, I2C_NACK_RECV_INT, UNMASK);
I2C_IntMask(I2C0_ID, I2C_RX_FIFO_READY_INT, UNMASK);
I2C_Int_Callback_Install(I2C0_ID, I2C_TRANS_END_INT, i2c_irq_done_read);
I2C_Int_Callback_Install(I2C0_ID, I2C_NACK_RECV_INT, i2c_irq_nack);
I2C_Int_Callback_Install(I2C0_ID, I2C_RX_FIFO_READY_INT, i2c_irq_rx_fifo_ready);
CPU_Interrupt_Enable(I2C_IRQn);
// Start
I2C_Enable(I2C0_ID);
// Wait for transfer in background
uint32_t result = 0;
xTaskNotifyWait(0xFFFFFFFF, 0xFFFFFFFF, &result, TICKS_100MS);
return result == 1;
}
bool FRToSI2C::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, uint8_t *p_buffer, uint16_t number_of_byte) {
if (!lock()) {
return false;
}
I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0};
BL_Err_Type err = ERROR;
i2cCfg.slaveAddr = DevAddress >> 1;
i2cCfg.stopEveryByte = DISABLE;
i2cCfg.subAddr = MemAddress;
i2cCfg.dataSize = number_of_byte;
i2cCfg.data = p_buffer;
i2cCfg.subAddrSize = 1; // one byte address
// Store handles for IRQ
IRQDataPointer = p_buffer;
IRQDataSizeLeft = number_of_byte;
IRQTaskWaitingHandle = xTaskGetCurrentTaskHandle();
IRQFailureMarker = false;
// Setup and run
I2C_Init(I2C0_ID, I2C_WRITE, &i2cCfg); // Setup hardware for the I2C init header with the device address
I2C_IntMask(I2C0_ID, I2C_TRANS_END_INT, UNMASK);
I2C_IntMask(I2C0_ID, I2C_NACK_RECV_INT, UNMASK);
I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, UNMASK);
I2C_Int_Callback_Install(I2C0_ID, I2C_TRANS_END_INT, i2c_irq_done);
I2C_Int_Callback_Install(I2C0_ID, I2C_NACK_RECV_INT, i2c_irq_nack);
I2C_Int_Callback_Install(I2C0_ID, I2C_TX_FIFO_READY_INT, i2c_irq_tx_fifo_low);
CPU_Interrupt_Enable(I2C_IRQn);
i2c_irq_tx_fifo_low();
// Start
I2C_Enable(I2C0_ID);
// Wait for transfer in background
uint32_t result = 0;
xTaskNotifyWait(0xFFFFFFFF, 0xFFFFFFFF, &result, TICKS_100MS);
return result == 1;
}
bool FRToSI2C::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) { return Mem_Write(DevAddress, pData[0], pData + 1, Size - 1); }
bool FRToSI2C::probe(uint16_t DevAddress) {
uint8_t temp[1];
return Mem_Read(DevAddress, 0x00, temp, sizeof(temp));
}
void FRToSI2C::I2C_Unstick() { unstick_I2C(); }
bool FRToSI2C::lock() {
if (I2CSemaphore == nullptr) {
return false;
}
return xSemaphoreTake(I2CSemaphore, TICKS_SECOND) == pdTRUE;
}
void FRToSI2C::unlock() { xSemaphoreGive(I2CSemaphore); }
bool FRToSI2C::writeRegistersBulk(const uint8_t address, const I2C_REG *registers, const uint8_t registersLength) {
for (int index = 0; index < registersLength; index++) {
if (!I2C_RegisterWrite(address, registers[index].reg, registers[index].val)) {
return false;
}
if (registers[index].pause_ms) {
delay_ms(registers[index].pause_ms);
}
}
return true;
}
bool FRToSI2C::wakePart(uint16_t DevAddress) {
// wakepart is a special case where only the device address is sent
if (!lock()) {
return false;
}
uint8_t temp[1] = {0};
I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0};
BL_Err_Type err = ERROR;
i2cCfg.slaveAddr = DevAddress >> 1;
i2cCfg.stopEveryByte = DISABLE;
i2cCfg.subAddr = 0;
i2cCfg.dataSize = 1;
i2cCfg.data = temp;
i2cCfg.subAddrSize = 0;
err = I2C_MasterReceiveBlocking(I2C0_ID, &i2cCfg);
bool res = err == SUCCESS;
if (!res) {
I2C_Unstick();
}
unlock();
return res;
}