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