diff options
author | Raviteja Narayanam <raviteja.narayanam@xilinx.com> | 2023-02-02 15:11:32 +0530 |
---|---|---|
committer | Wolfram Sang <wsa@kernel.org> | 2023-02-03 17:34:00 +0100 |
commit | 2fd5cf352efa0c62dd20d1e046bc8767395b1ec0 (patch) | |
tree | d20c20bf784052d179526fd7e0ab1f6c6e46554b /drivers/i2c | |
parent | 813eac4fcb839bdf22aacfda0f2713fe50a82974 (diff) |
i2c: xiic: Switch to Xiic standard mode for i2c-read
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>
Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Acked-by: Michal Simek <michal.simek@amd.com>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-xiic.c | 96 |
1 files changed, 57 insertions, 39 deletions
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 86da622e060c..edc64b79e293 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -32,6 +32,7 @@ #include <linux/pm_runtime.h> #define DRIVER_NAME "xiic-i2c" +#define DYNAMIC_MODE_READ_BROKEN_BIT BIT(0) enum xilinx_i2c_state { STATE_DONE, @@ -62,6 +63,7 @@ enum xiic_endian { * @singlemaster: Indicates bus is single master * @dynamic: Mode of controller * @prev_msg_tx: Previous message is Tx + * @quirks: To hold platform specific bug info */ struct xiic_i2c { struct device *dev; @@ -80,6 +82,11 @@ struct xiic_i2c { bool singlemaster; bool dynamic; bool prev_msg_tx; + u32 quirks; +}; + +struct xiic_version_data { + u32 quirks; }; #define XIIC_MSB_OFFSET 0 @@ -878,7 +885,8 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c) static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num) { - int ret; + bool broken_read, max_read_len, smbus_blk_read; + int ret, count; mutex_lock(&i2c->lock); @@ -891,6 +899,34 @@ static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num) i2c->nmsgs = num; init_completion(&i2c->completion); + /* Decide standard mode or Dynamic mode */ + i2c->dynamic = true; + + /* Initialize prev message type */ + i2c->prev_msg_tx = false; + + /* + * 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++) { + 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; + } + } + ret = xiic_reinit(i2c); if (!ret) __xiic_start_xfer(i2c); @@ -913,36 +949,6 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (err < 0) return err; - /* Decide standard mode or Dynamic mode */ - i2c->dynamic = true; - - /* Initialize prev message type */ - i2c->prev_msg_tx = false; - - /* - * If number of messages is 1 and read length is > 255 bytes, - * enter standard mode - */ - - if (i2c->nmsgs == 1 && (i2c->tx_msg->flags & I2C_M_RD) && - i2c->tx_msg->len > MAX_READ_LENGTH_DYNAMIC) { - i2c->dynamic = false; - } else if (i2c->nmsgs > 1) { - int count; - - /* - * If number of messages is more than 1 and one of them is - * a read message, enter standard mode. Since repeated start - * operation in dynamic mode read is not happenning - */ - for (count = 0; count < i2c->nmsgs; count++) { - if (i2c->tx_msg[count].flags & I2C_M_RD) { - i2c->dynamic = false; - break; - } - } - } - err = xiic_start_xfer(i2c, msgs, num); if (err < 0) { dev_err(adap->dev.parent, "Error xiic_start_xfer\n"); @@ -985,10 +991,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; @@ -998,6 +1017,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)) @@ -1112,14 +1138,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); |