Skip to content

Commit

Permalink
i2c: xiic: Switch to Xiic standard mode for i2c-read
Browse files Browse the repository at this point in the history
Xilinx I2C IP has two modes of operation, both of which implement
I2C transactions. The only difference from sw perspective is the
programming sequence for these modes.
Dynamic mode  -> Simple to program, less number of steps in sequence.
Standard mode -> Gives flexibility, more number of steps in sequence.

In dynamic mode, during the i2c-read transactions, if there is a
delay(> 200us) between the register writes (address & byte count),
read transaction fails. On a system with load, this scenario is
occurring frequently.
To avoid this, switch to standard mode if there is a read request.

Added a quirk to identify the IP version effected by this and follow
the standard mode.

Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>
State: pending
  • Loading branch information
Raviteja Narayanam authored and Michal Simek committed Mar 22, 2021
1 parent 2e743bd commit 744f0ae
Showing 1 changed file with 43 additions and 13 deletions.
56 changes: 43 additions & 13 deletions drivers/i2c/busses/i2c-xiic.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

#define DRIVER_NAME "xiic-i2c"

#define DYNAMIC_MODE_READ_BROKEN_BIT BIT(0)

enum xilinx_i2c_state {
STATE_DONE,
STATE_ERROR,
Expand Down Expand Up @@ -63,6 +65,7 @@ enum xiic_endian {
* @dynamic: Mode of controller
* @prev_msg_tx: Previous message is Tx
* @smbus_block_read: Flag to handle block read
* @quirks: To hold platform specific bug info
*/
struct xiic_i2c {
struct device *dev;
Expand All @@ -82,6 +85,11 @@ struct xiic_i2c {
bool dynamic;
bool prev_msg_tx;
bool smbus_block_read;
u32 quirks;
};

struct xiic_version_data {
u32 quirks;
};

#define XIIC_MSB_OFFSET 0
Expand Down Expand Up @@ -1037,6 +1045,7 @@ static int xiic_start_xfer(struct xiic_i2c *i2c)

static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
bool broken_read, max_read_len, smbus_blk_read;
struct xiic_i2c *i2c = i2c_get_adapdata(adap);
int err, count;

Expand All @@ -1061,13 +1070,22 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
i2c->prev_msg_tx = false;

/*
* Enter standard mode only when read length is > 255 bytes or
* for smbus_block_read transaction
* Scan through nmsgs, use dynamic mode when none of the below three
* conditions occur. We need standard mode even if one condition holds
* true in the entire array of messages in a single transfer.
* If read transaction as dynamic mode is broken for delayed reads
* in xlnx,axi-iic-2.0 / xlnx,xps-iic-2.00.a IP versions.
* If read length is > 255 bytes.
* If smbus_block_read transaction.
*/
for (count = 0; count < i2c->nmsgs; count++) {
if (((i2c->tx_msg[count].flags & I2C_M_RD) &&
i2c->tx_msg[count].len > MAX_READ_LENGTH_DYNAMIC) ||
(i2c->tx_msg[count].flags & I2C_M_RECV_LEN)) {
broken_read = (i2c->quirks & DYNAMIC_MODE_READ_BROKEN_BIT) &&
(i2c->tx_msg[count].flags & I2C_M_RD);
max_read_len = (i2c->tx_msg[count].flags & I2C_M_RD) &&
(i2c->tx_msg[count].len > MAX_READ_LENGTH_DYNAMIC);
smbus_blk_read = (i2c->tx_msg[count].flags & I2C_M_RECV_LEN);

if (broken_read || max_read_len || smbus_blk_read) {
i2c->dynamic = false;
break;
}
Expand Down Expand Up @@ -1113,10 +1131,23 @@ static const struct i2c_adapter xiic_adapter = {
.algo = &xiic_algorithm,
};

static const struct xiic_version_data xiic_2_00 = {
.quirks = DYNAMIC_MODE_READ_BROKEN_BIT,
};

#if defined(CONFIG_OF)
static const struct of_device_id xiic_of_match[] = {
{ .compatible = "xlnx,xps-iic-2.00.a", .data = &xiic_2_00 },
{},
};
MODULE_DEVICE_TABLE(of, xiic_of_match);
#endif

static int xiic_i2c_probe(struct platform_device *pdev)
{
struct xiic_i2c *i2c;
struct xiic_i2c_platform_data *pdata;
const struct of_device_id *match;
struct resource *res;
int ret, irq;
u8 i;
Expand All @@ -1126,6 +1157,13 @@ static int xiic_i2c_probe(struct platform_device *pdev)
if (!i2c)
return -ENOMEM;

match = of_match_node(xiic_of_match, pdev->dev.of_node);
if (match && match->data) {
const struct xiic_version_data *data = match->data;

i2c->quirks = data->quirks;
}

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->base))
Expand Down Expand Up @@ -1237,14 +1275,6 @@ static int xiic_i2c_remove(struct platform_device *pdev)
return 0;
}

#if defined(CONFIG_OF)
static const struct of_device_id xiic_of_match[] = {
{ .compatible = "xlnx,xps-iic-2.00.a", },
{},
};
MODULE_DEVICE_TABLE(of, xiic_of_match);
#endif

static int __maybe_unused xiic_i2c_runtime_suspend(struct device *dev)
{
struct xiic_i2c *i2c = dev_get_drvdata(dev);
Expand Down

0 comments on commit 744f0ae

Please sign in to comment.