Загрузка данных


#include <linux/delay.h>

static int sysconfig_i2c_bitstream_burst_write(struct sysconfig_priv *priv, const char *buf, size_t len)
{
    struct i2c_client *client = to_i2c_client(priv->dev);
    const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST; // 0x7A, 0x00, 0x00, 0x00
    size_t cmd_len = sizeof(lsc_bitstream_burst);
    size_t chunk_size = 256; /* Строго 256 байт - аппаратный лимит FIFO LPI2C NXP */
    size_t offset = 0;
    int ret;

    if (len == 0)
        return 0;

    /* Вычисляем выравнивание общего объема данных битстрима по границе 4 байт */
    size_t aligned_len = (len + 3) & ~3;
    size_t total_padding = aligned_len - len;

    dev_info(priv->dev, "Starting segmented burst: len=%zu, aligned=%zu, padding=%zu\n", 
             len, aligned_len, total_padding);

    /* 
     * ШАГ 1: Отправляем команду инициализации BURST (0x7A 0x00 0x00 0x00).
     * Отправляем ее как самостоятельную короткую транзакцию.
     */
    ret = i2c_master_send(client, lsc_bitstream_burst, cmd_len);
    if (ret != cmd_len) {
        dev_err(priv->dev, "Failed to send burst command: %d\n", ret);
        return (ret < 0) ? ret : -EIO;
    }

    /* Аппаратная микропауза для ПЛИС, чтобы она успела переключить режим десериализатора */
    usleep_range(1000, 2000);

    /* 
     * ШАГ 2: Передаем бинарный поток порциями строго по 256 байт.
     * Мы используем i2c_master_send, что генерирует стандартные START/STOP на каждую порцию,
     * защищая eDMA драйвер i.MX95 от переполнения 8-битного счетчика длины данных.
     */
    while (offset < len) {
        size_t current_chunk = min(chunk_size, len - offset);

        /* Если это самый последний чанк и для него требуется выравнивание NOP-байтами */
        if (offset + current_chunk == len && total_padding > 0) {
            size_t padded_size = current_chunk + total_padding;
            u8 *padded_buf = kmalloc(padded_size, GFP_KERNEL);
            if (!padded_buf)
                return -ENOMEM;

            memcpy(padded_buf, buf + offset, current_chunk);
            memset(padded_buf + current_chunk, 0xFF, total_padding);

            dev_dbg(priv->dev, "Sending final padded chunk: %zu -> %zu bytes\n", 
                    current_chunk, padded_size);

            ret = i2c_master_send(client, padded_buf, padded_size);
            kfree(padded_buf);

            if (ret != padded_size) {
                dev_err(priv->dev, "Padded I2C write failed at offset %zu: %d\n", offset, ret);
                return (ret < 0) ? ret : -EIO;
            }
        } else {
            /* Обычный промежуточный идеально ровный 256-байтный блок */
            ret = i2c_master_send(client, buf + offset, current_chunk);
            if (ret != current_chunk) {
                dev_err(priv->dev, "I2C block write failed at offset %zu: %d\n", offset, ret);
                return (ret < 0) ? ret : -EIO;
            }
        }

        offset += current_chunk;

        /* 
         * КРИТИЧЕСКИ ВАЖНО: Даем контроллеру LPI2C i.MX95 время полностью сбросить 
         * прерывания окончания транзакции eDMA/PIO и исключаем Clock Stretching со стороны ПЛИС.
         */
        usleep_range(100, 200); 
    }

    dev_info(priv->dev, "FPGA bitstream sent successfully. Checking validation status...\n");
    return 0;
}