int32_t I2CMaster_TransferSequentialAsync_UserData()

in IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/I2CMaster.c [241:414]


int32_t I2CMaster_TransferSequentialAsync_UserData(
    I2CMaster *handle, uint16_t address,
    const I2C_Transfer *transfer, uint32_t count,
    void (*callback)(int32_t status, uintptr_t count, void* userData),
    void *userData)
{
    if (!handle) {
        return ERROR_PARAMETER;
    }
    if (!handle->open) {
        return ERROR_HANDLE_CLOSED;
    }
    if (handle->transfer) {
        return ERROR_BUSY;
    }

    // We don't support more than 7-bit addressing.
    if ((address >> 7) != 0) {
        return ERROR_UNSUPPORTED;
    }

    if (!transfer || (count == 0)) {
        return ERROR_PARAMETER;
    }

    // It's up to the user to group transfers of the same type
    if (count > MT3620_I2C_QUEUE_DEPTH) {
        return ERROR_UNSUPPORTED;
    }

    bool onBus = true;
    unsigned wcnt  = 0, rcnt  = 0;
    unsigned wdata = 0, rdata = 0;
    unsigned i;
    for (i = 0; i < count; i++) {
        if (transfer[i].length > MT3620_I2C_PACKET_SIZE_MAX) {
            return ERROR_UNSUPPORTED;
        }

        // Transfer must be a read or a write.
        if (!transfer[i].writeData && !transfer[i].readData) {
            return ERROR_PARAMETER;
        }

        // I2C doesn't support duplex transfers.
        if (transfer[i].writeData && transfer[i].readData) {
            return ERROR_UNSUPPORTED;
        }

        if (transfer[i].writeData) {
            if (!addrOnBus(transfer[i].writeData)) {
                onBus = false;
            }

            wcnt++;
            wdata += transfer[i].length;
        }

        if (transfer[i].readData) {
            if (!addrOnBus(transfer[i].readData)) {
                onBus = false;
            }

            rcnt++;
            rdata += transfer[i].length;
        }
    }

    if (MT3620_I2C_FIELD_READ(handle->id, mm_status, bus_busy)) {
        return ERROR_BUSY;
    }

    bool useDMA = false;
    if ((wdata > MT3620_I2C_TX_FIFO_DEPTH)
        || (rdata > MT3620_I2C_RX_FIFO_DEPTH)) {
        // DMA can only access data on the bus (i.e. not TCM).
        if (!onBus) {
            return ERROR_DMA_SOURCE;
        }

        // DMA can queue a maximum of two transactions in each direction.
        if ((wcnt > 2) || (rcnt > 2)) {
            return ERROR_UNSUPPORTED;
        }

        useDMA = true;
    }

    handle->callbackUser = callback;
    handle->userData     = userData;
    handle->useDMA       = useDMA;
    handle->txQueued     = wdata;
    handle->rxQueued     = rdata;
    handle->transfer     = transfer;
    handle->count        = count;

    mt3620_i2c[handle->id]->mm_slave_id = address;

    for (i = 0; i < count; i++) {
        mt3620_i2c[handle->id]->mm_cnt_byte_val_pk[i] = transfer[i].length;
    }

    mt3620_i2c_mm_pack_con0_t mm_pack_con0 = { .mask = mt3620_i2c[handle->id]->mm_pack_con0 };
    mm_pack_con0.mm_pack_rw0 = (transfer[0].readData != NULL);
    if (count >= 2) mm_pack_con0.mm_pack_rw1 = (transfer[1].readData != NULL);
    if (count >= 3) mm_pack_con0.mm_pack_rw2 = (transfer[2].readData != NULL);
    mm_pack_con0.mm_pack_val = (count - 1);
    mt3620_i2c[handle->id]->mm_pack_con0 = mm_pack_con0.mask;

    if (useDMA) {
        volatile mt3620_dma_t * const tx_dma = &mt3620_dma[MT3620_I2C_DMA_TX(handle->id)];
        volatile mt3620_dma_t * const rx_dma = &mt3620_dma[MT3620_I2C_DMA_RX(handle->id)];

        unsigned w = 0, r = 0;
        for (i = 0; i < count; i++) {
            if (transfer[i].writeData) {
                if (w++ == 0) {
                    tx_dma->pgmaddr = (uint32_t *)transfer[i].writeData;
                    tx_dma->wppt    = transfer[i].length;
                    tx_dma->count   = transfer[i].length;
                } else {
                    tx_dma->wpto    = (uint32_t *)transfer[i].writeData;
                    tx_dma->count  += transfer[i].length;
                }
            } else {
                if (r++ == 0) {
                    rx_dma->pgmaddr = transfer[i].readData;
                    rx_dma->wppt    = transfer[i].length;
                    rx_dma->count   = transfer[i].length;
                } else {
                    rx_dma->wpto    = transfer[i].readData;
                    rx_dma->count  += transfer[i].length;
                }
            }
        }

        if (wcnt > 0) {
            MT3620_DMA_FIELD_WRITE(MT3620_I2C_DMA_TX(handle->id), con, wpen, (wcnt > 1));

            // Start DMA TX transfer.
            MT3620_DMA_FIELD_WRITE(MT3620_I2C_DMA_TX(handle->id), start, str, true);
        }

        if (rcnt > 0) {
            MT3620_DMA_FIELD_WRITE(MT3620_I2C_DMA_RX(handle->id), con, wpen, (rcnt > 1));

            // Start DMA RX transfer.
            MT3620_DMA_FIELD_WRITE(MT3620_I2C_DMA_RX(handle->id), start, str, true);
        }
    } else {
        // Pre-fill TX buffer with data.
        for (i = 0; i < count; i++) {
            const uint8_t *writeData = transfer[i].writeData;
            if (writeData) {
                unsigned j;
                for (j = 0; j < transfer[i].length; j++) {
                    mt3620_i2c[handle->id]->mm_fifo_data = writeData[j];
                }
            }
        }
    }

    // Wait until I2C is ready after setting config
    while (!MT3620_I2C_FIELD_READ(handle->id, mm_status, mm_start_ready)) {
        // Do nothing.
    }

    mt3620_i2c_mm_con0_t mm_con0 = { .mask = mt3620_i2c[handle->id]->mm_con0 };
    mm_con0.mm_gmode    = true;
    mm_con0.mm_start_en = true;
    mt3620_i2c[handle->id]->mm_con0 = mm_con0.mask;

    return ERROR_NONE;
}