#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;
}