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


static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
	unsigned int temp;
	int i, result;
	bool is_nostart;

printk("%s(%d) num=%d\n",__FUNCTION__,__LINE__,num);
	result = lpi2c_imx_master_enable(lpi2c_imx);
	if (result)
		return result;

	for (i = 0; i < num; i++) {
		is_nostart = !!(msgs[i].flags & I2C_M_NOSTART);

printk("%s(%d) %d)is_nostart=%d len=%u\n",__FUNCTION__,__LINE__,i,is_nostart, msgs[i].len);
		/* 
		 * Аппаратный СТАРТ генерируем ТОЛЬКО для первого сообщения 
		 * или если флаг I2C_M_NOSTART явно ОТСУТСТВУЕТ.
		 */
		if (!is_nostart) {
			result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
			if (result)
				goto disable;
		} else {
			/* 
			 * Если это NOSTART, ПЛИС уже слушает шину.
			 * Сбрасываем счетчик доставленных байт текущего msg для PIO/DMA логики.
			 */
			 printk("%s(%d) NO_START!!!\n",__FUNCTION__,__LINE__);
			lpi2c_imx->delivered = 0;
		}

		/* Quick SMBus check */
		if (num == 1 && msgs[0].len == 0)
			goto stop;

		lpi2c_imx->rx_buf = NULL;
		lpi2c_imx->tx_buf = NULL;
		lpi2c_imx->delivered = 0;
		lpi2c_imx->msglen = msgs[i].len;
		init_completion(&lpi2c_imx->complete);

		/* 
		 * ВНИМАНИЕ: Если это NOSTART-сообщение, принудительно шлем его через PIO.
		 * Это защищает от микросекундных пауз на переключение eDMA-каналов i.MX95,
		 * которые вызывают Clock Stretching и BSE Timeout в ПЛИС.
		 */
		if (is_use_dma(lpi2c_imx, &msgs[i]) && !is_nostart) {
			result = lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]);
			if (result && lpi2c_imx->dma->using_pio_mode)
				result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
		} else {
			result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
		}

		if (result)
			goto stop;

		if (!(msgs[i].flags & I2C_M_RD)) {
			result = lpi2c_imx_txfifo_empty(lpi2c_imx);
			if (result)
				goto stop;
		}
	}

stop:
	/* 
	 * Аппаратный СТОП генерируется только один раз в самом конце, 
	 * когда весь массив сообщений (Команда + Куски битстрима) полностью передан.
	 */
	lpi2c_imx_stop(lpi2c_imx);

	temp = readl(lpi2c_imx->base + LPI2C_MSR);
	if ((temp & MSR_NDF) && !result)
		result = -EIO;

disable:
	lpi2c_imx_master_disable(lpi2c_imx);

	dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
		(result < 0) ? "error" : "success msg",
		(result < 0) ? result : num);

	return (result < 0) ? result : num;
}


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

Логи:
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1041) num=2
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1049) 0)is_nostart=0 len=4
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1049) 1)is_nostart=1 len=4096
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1063) NO_START!!!
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1041) num=1
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1049) 0)is_nostart=1 len=4096
Oct 12 07:47:28 imx95-19x19-lpddr5-evk kernel: lpi2c_imx_xfer(1063) NO_START!!!
Oct 12 07:47:30 imx95-19x19-lpddr5-evk kernel: fpga_csi_bridge 2-0040: I2C continuous burst write failed at offset 4096: -110