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


#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/slab.h>

#include "lattice-sysconfig.h"

static int sysconfig_i2c_cmd_transfer(struct sysconfig_priv *priv, const void *tx_buf, size_t tx_len, void *rx_buf, size_t rx_len)
{
    struct i2c_client *client = to_i2c_client(priv->dev);
    struct i2c_msg msgs[2];
    int num_msgs = 0;
    int ret;

    /* 1. Write message (command and/or data payload) */
    if (tx_len > 0) {
        msgs[num_msgs].addr = client->addr;
        msgs[num_msgs].flags = client->flags & I2C_M_TEN;
        msgs[num_msgs].len = tx_len;
        msgs[num_msgs].buf = (u8 *)tx_buf;
        num_msgs++;
    }

    /* 2. Read message (if a response like device status is expected) */
    if (rx_len > 0) {
        msgs[num_msgs].addr = client->addr;
        /* I2C_M_RD flag switches the bus direction to read */
        msgs[num_msgs].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
        msgs[num_msgs].len = rx_len;
        msgs[num_msgs].buf = rx_buf;
        num_msgs++;
    }

    if (num_msgs == 0)
        return 0;

    /* Perform an atomic combined transaction */
    ret = i2c_transfer(client->adapter, msgs, num_msgs);
    if (ret < 0)
        return ret;

    return (ret == num_msgs) ? 0 : -EIO;
}

static int sysconfig_i2c_bitstream_burst_init(struct sysconfig_priv *priv)
{
    const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST;
    struct i2c_client *client = to_i2c_client(priv->dev);
    int ret;

    /* 
     * Send the initial burst write command to the FPGA.
     * Unlike SPI, explicit bus locking for the entire duration is 
     * handled sequentially via standard I2C multi-message handling.
     */
    ret = i2c_master_send(client, lsc_bitstream_burst, sizeof(lsc_bitstream_burst));
    if (ret < 0)
        return ret;

    return (ret == sizeof(lsc_bitstream_burst)) ? 0 : -EIO;
}

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);
    struct i2c_msg msg;
    int ret;

    if (len == 0)
        return 0;

    /*
     * IMPORTANT: Stream the remaining bitstream chunks.
     * The I2C_M_NOSTART flag instructs the i.MX95 I2C controller NOT to 
     * generate a repeated START condition or re-send the slave address.
     * The data flows continuously as an ongoing payload sequence.
     */
    msg.addr = client->addr;
    msg.flags = (client->flags & I2C_M_TEN) | I2C_M_NOSTART;
    msg.len = len;
    msg.buf = (u8 *)buf;

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret < 0)
        return ret;

    return (ret == 1) ? 0 : -EIO;
}

static int sysconfig_i2c_bitstream_burst_complete(struct sysconfig_priv *priv)
{
    /* 
     * In I2C, the stream sequence naturally completes when we stop 
     * transferring blocks with the I2C_M_NOSTART flag. The final 
     * transaction automatically releases the bus with a STOP condition.
     */
    return 0;
}

static int sysconfig_i2c_probe(struct i2c_client *client)
{
    struct device *dev = &client->dev;
    struct sysconfig_priv *priv;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->dev = dev;
    priv->command_transfer = sysconfig_i2c_cmd_transfer;
    priv->bitstream_burst_write_init = sysconfig_i2c_bitstream_burst_init;
    priv->bitstream_burst_write = sysconfig_i2c_bitstream_burst_write;
    priv->bitstream_burst_write_complete = sysconfig_i2c_bitstream_burst_complete;

    /* Call the generic initialization sequence from lattice-sysconfig.c */
    return sysconfig_probe(priv);
}

static const struct i2c_device_id sysconfig_i2c_ids[] = {
    { "sysconfig-crosslinknx", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, sysconfig_i2c_ids);

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id sysconfig_of_ids[] = {
    { .compatible = "lattice,sysconfig-crosslinknx" },
    { }
};
MODULE_DEVICE_TABLE(of, sysconfig_of_ids);
#endif

static struct i2c_driver lattice_sysconfig_driver = {
    .probe = sysconfig_i2c_probe,
    .id_table = sysconfig_i2c_ids,
    .driver = {
        .name = "lattice_sysconfig_i2c_fpga_mgr",
        .of_match_table = of_match_ptr(sysconfig_of_ids),
    },
};
module_i2c_driver(lattice_sysconfig_driver);

MODULE_DESCRIPTION("Lattice sysCONFIG Slave I2C FPGA Manager");
MODULE_LICENSE("GPL");