Загрузка данных
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);
/* Массив из 2-х сообщений: 1-е с командой (со START), 2-е с флагом NOSTART */
struct i2c_msg msgs[2];
const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST; // 0x7A, 0x00, 0x00, 0x00
size_t offset = 0;
int ret;
if (len == 0)
return 0;
/* Вычисляем общее необходимое выравнивание для всего объема данных */
size_t aligned_len = (len + 3) & ~3;
size_t total_padding = aligned_len - len;
printk("%s(%d) Total length = %zu, Aligned length = %zu (Total padding: %zu bytes)\n",
__FUNCTION__, __LINE__, len, aligned_len, total_padding);
/*
* ШАГ 1: Отправляем команду начала BURST и ПЕРВЫЙ кусок данных в одной сессии i2c_transfer.
* Это гарантирует, что между командой 0x7A и данными не будет STOP-условия.
*/
size_t first_chunk = min((size_t)4096, len);
// Сообщение 0: Команда инициализации BURST (со START)
msgs[0].addr = client->addr;
msgs[0].flags = client->flags & I2C_M_TEN;
msgs[0].len = sizeof(lsc_bitstream_burst);
msgs[0].buf = (u8 *)lsc_bitstream_burst;
// Сообщение 1: Первый кусок прошивки (удерживаем шину через NOSTART)
msgs[1].addr = client->addr;
msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_NOSTART;
msgs[1].len = first_chunk;
msgs[1].buf = (u8 *)buf;
// Передаем оба сообщения за один проход ядра. Контроллер i.MX95 выдаст START,
// передаст команду, НЕ выдавая STOP передаст первый блок данных.
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret != 2) {
dev_err(priv->dev, "I2C initial burst command + data failed: %d\n", ret);
return (ret < 0) ? ret : -EIO;
}
offset += first_chunk;
/*
* ШАГ 2: Досылаем оставшуюся часть прошивки блоками по 4КБ с контролем Padding на финише.
*/
while (offset < len) {
size_t chunk_size = min((size_t)4096, len - offset);
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = (client->flags & I2C_M_TEN) | I2C_M_NOSTART;
/* Если это самый последний кусок и для него требуется выравнивание */
if (offset + chunk_size == len && total_padding > 0) {
size_t padded_chunk_size = chunk_size + total_padding;
u8 *padded_buf = kmalloc(padded_chunk_size, GFP_KERNEL);
if (!padded_buf)
return -ENOMEM;
/* Копируем чистый остаток данных и добиваем хвост байтами NOP (0xFF) */
memcpy(padded_buf, buf + offset, chunk_size);
memset(padded_buf + chunk_size, 0xFF, total_padding);
msg.len = padded_chunk_size;
msg.buf = padded_buf;
printk("%s(%d) Applying padding to last chunk: %zu -> %zu bytes\n",
__FUNCTION__, __LINE__, chunk_size, padded_chunk_size);
ret = i2c_transfer(client->adapter, &msg, 1);
kfree(padded_buf); // Освобождаем временный буфер сразу после отправки
} else {
/* Обычный промежуточный или идеально ровный чанк */
msg.len = chunk_size;
msg.buf = (u8 *)(buf + offset);
ret = i2c_transfer(client->adapter, &msg, 1);
}
if (ret != 1) {
dev_err(priv->dev, "I2C continuous burst write failed at offset %zu: %d\n", offset, ret);
return (ret < 0) ? ret : -EIO;
}
offset += chunk_size;
}
printk("%s(%d) Burst stream successfully transmitted with padding\n", __FUNCTION__, __LINE__);
return 0;
}