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


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