I2C_XFER_V2 is a new API that allows I2C clients to get the detailed
report in case of transmission failure. Previously, the only information
returned by I2C bus controller was the error code; there was no way to
find out how many messages or bytes in a certain message have been sent
or received until the fault condition occurred.

This commit introduces support of this feature in i2c-nomadik driver.

Signed-off-by: Dmitry Guzman <[email protected]>
---
 drivers/i2c/busses/i2c-nomadik.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 
9cff0c2757fafeaf809395e02a5e754570f65e08..1cf03d634fdc856dc335a58597e0fd31ab077078
 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -197,6 +197,7 @@ struct i2c_nmk_client {
  * @stop: stop condition.
  * @xfer_wq: xfer done wait queue.
  * @result: controller propogated result.
+ * @bytes_cplt: number of bytes completed in the message that caused a fault.
  */
 struct nmk_i2c_dev {
        struct i2c_vendor_data          *vendor;
@@ -216,6 +217,7 @@ struct nmk_i2c_dev {
        int                             stop;
        struct wait_queue_head          xfer_wq;
        int                             result;
+       int                             bytes_cplt;
 };
 
 /* controller's abort causes */
@@ -529,6 +531,8 @@ static int read_i2c(struct nmk_i2c_dev *priv, u16 flags)
        int status = 0;
        bool xfer_done;
 
+       priv->cli.xfer_bytes = 0;
+
        mcr = load_i2c_mcr_reg(priv, flags);
        writel(mcr, priv->virtbase + I2C_MCR);
 
@@ -653,6 +657,7 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 
flags)
 {
        int status;
 
+       priv->bytes_cplt = 0;
        if (flags & I2C_M_RD) {
                /* read operation */
                priv->cli.operation = I2C_READ;
@@ -678,6 +683,16 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 
flags)
                        status = priv->result;
                }
 
+               if (flags & I2C_M_RD) {
+                       /* For READ messages, return the number of bytes read 
from FIFO */
+                       priv->bytes_cplt = priv->cli.xfer_bytes;
+               } else {
+                       /* For WRITE messages, return the number of bytes sent 
on bus */
+                       priv->bytes_cplt = FIELD_GET(I2C_SR_LENGTH, i2c_sr);
+                       /* LENGTH value includes the last byte that has not 
been sent or ACKed */
+                       if (priv->bytes_cplt > 0)
+                               priv->bytes_cplt--;
+               }
                init_hw(priv);
 
                status = status ? status : priv->result;
@@ -687,10 +702,11 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 
flags)
 }
 
 /**
- * nmk_i2c_xfer() - I2C transfer function used by kernel framework
+ * nmk_i2c_xfer_v2() - I2C transfer function used by kernel framework
  * @i2c_adap: Adapter pointer to the controller
  * @msgs: Pointer to data to be written.
  * @num_msgs: Number of messages to be executed
+ * @report: Pointer to transfer report to be written.
  *
  * This is the function called by the generic kernel i2c_transfer()
  * or i2c_smbus...() API calls. Note that this code is protected by the
@@ -733,14 +749,16 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 
flags)
  * please use the i2c_smbus_read_i2c_block_data()
  * or i2c_smbus_write_i2c_block_data() API
  */
-static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
-               struct i2c_msg msgs[], int num_msgs)
+static int nmk_i2c_xfer_v2(struct i2c_adapter *i2c_adap,
+               struct i2c_msg msgs[], int num_msgs,
+               struct i2c_transfer_report *report)
 {
        int status = 0;
        int i;
        struct nmk_i2c_dev *priv = i2c_get_adapdata(i2c_adap);
 
        pm_runtime_get_sync(&priv->adev->dev);
+       priv->bytes_cplt = 0;
 
        /* setup the i2c controller */
        setup_i2c_controller(priv);
@@ -760,10 +778,17 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
        pm_runtime_put_sync(&priv->adev->dev);
 
        /* return the no. messages processed */
-       if (status)
+       if (status) {
+               report->msgs_cplt = i;
+               report->bytes_cplt = priv->bytes_cplt;
+               report->fault_msg_idx = i;
                return status;
-       else
+       } else {
+               report->msgs_cplt = num_msgs;
+               report->bytes_cplt = 0;
+               report->fault_msg_idx = num_msgs;
                return num_msgs;
+       }
 }
 
 /**
@@ -1014,7 +1039,7 @@ static unsigned int nmk_i2c_functionality(struct 
i2c_adapter *adap)
 }
 
 static const struct i2c_algorithm nmk_i2c_algo = {
-       .xfer = nmk_i2c_xfer,
+       .xfer_v2 = nmk_i2c_xfer_v2,
        .functionality = nmk_i2c_functionality
 };
 

-- 
2.43.0


Reply via email to