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


#include <linux/io.h>
#include <linux/delay.h>

/**
 * lpi2c_imx_xfer_burst - Прямая монолитная трансляция прошивки ПЛИС в FIFO LPI2C
 * @adapter: указатель на I2C адаптер шины
 * @client_addr: 7-битный I2C адрес ПЛИС Lattice
 * @cmd_buf: указатель на команду инициализации BURST (0x7A, 0x00, 0x00, 0x00)
 * @cmd_len: длина команды (обычно 4 байта)
 * @data_buf: указатель на огромный плоский буфер битстрима (808 КБ)
 * @data_len: точный размер битстрима (size_t снимает ограничение 64 КБ)
 */
int lpi2c_imx_xfer_burst(struct i2c_adapter *adapter, u16 client_addr,
			 const u8 *cmd_buf, size_t cmd_len,
			 const u8 *data_buf, size_t data_len)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
	void __iomem *base = lpi2c_imx->base;
	size_t offset = 0;
	unsigned int txcnt, temp;
	int i, result;

	if (!data_buf || data_len == 0)
		return -EINVAL;

	/* 1. Аппаратно включаем мастер-контроллер и тактирование */
	result = lpi2c_imx_master_enable(lpi2c_imx);
	if (result)
		return result;

	/* 2. Генерируем единственный СТАРТ и передаем адрес устройства */
	/* 0x400 - аппаратный код команды START/ADDR в регистре MTDR NXP */
	writel(0x400 | (client_addr << 1), base + LPI2C_MTDR);

	/* 3. Выталкиваем команду префикса BURST (0x7A ...) */
	for (i = 0; i < cmd_len; i++) {
		while ((readl(base + LPI2C_MFSR) & 0xff) >= lpi2c_imx->txfifosize)
			cpu_relax();
		writel(cmd_buf[i], base + LPI2C_MTDR);
	}

	/* 4. Льем ВСЕ 808 КБ прошивки непрерывным «сырым» PIO-потоком напрямую в FIFO */
	/* Никаких циклов по сообщениям, никакого переполнения __u16, никакого мусора */
	while (offset < data_len) {
		txcnt = readl(base + LPI2C_MFSR) & 0xff;
		while (txcnt < lpi2c_imx->txfifosize && offset < data_len) {
			writel(data_buf[offset++], base + LPI2C_MTDR);
			txcnt++;
		}
		cpu_relax();
	}

	/* 5. Досылаем Padding байты (0xFF) до выравнивания на 4 байта */
	size_t aligned_len = (data_len + 3) & ~3;
	size_t padding = aligned_len - data_len;
	for (i = 0; i < padding; i++) {
		while ((readl(base + LPI2C_MFSR) & 0xff) >= lpi2c_imx->txfifosize)
			cpu_relax();
		writel(0xFF, base + LPI2C_MTDR);
	}

	/* 6. Ждем, пока FIFO полностью опустеет и все данные физически уйдут в шину */
	while ((readl(base + LPI2C_MSR) & MSR_TDF) == 0)
		cpu_relax();

	/* 7. Генерируем аппаратный СТОП (Команда 0x200 в MTDR) строго в самом конце */
	writel(0x200, base + LPI2C_MTDR);

	/* Проверяем аппаратные флаги ошибок NACK / Arbitration Lost */
	temp = readl(base + LPI2C_MSR);
	if (temp & MSR_NDF)
		result = -EIO; /* Слейв вернул NACK */

	/* 8. Корректно выключаем мастер контроллера */
	lpi2c_imx_master_disable(lpi2c_imx);

	return result;
}
EXPORT_SYMBOL_GPL(lpi2c_imx_xfer_burst);