[v2,1/1] i2c: iproc: Add i2c repeated start capability
diff mbox series

Message ID 1569825869-30640-1-git-send-email-rayagonda.kokatanur@broadcom.com
State Under Review
Headers show
Series
  • [v2,1/1] i2c: iproc: Add i2c repeated start capability
Related show

Commit Message

Rayagonda Kokatanur Sept. 30, 2019, 6:44 a.m. UTC
From: Lori Hikichi <lori.hikichi@broadcom.com>

Enable handling of i2c repeated start. The current code
handles a multi msg i2c transfer as separate i2c bus
transactions. This change will now handle this case
using the i2c repeated start protocol. The number of msgs
in a transfer is limited to two, and must be a write
followed by a read.

Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
Signed-off-by: Ray Jui <ray.jui@broadcom.com>
Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>
---
changes from v1:
 - Address following review comments from Wolfarm Sang,
   Use i2c_8bit_addr_from_msg() api instead of decoding i2c_msg struct and
   remove check against number of i2c message as it will be taken care
   by core using quirks flags. 

 drivers/i2c/busses/i2c-bcm-iproc.c | 63 ++++++++++++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 13 deletions(-)

Comments

Rayagonda Kokatanur Oct. 10, 2019, 9:32 a.m. UTC | #1
Hi Wolfram,

Did you get a chance to review this patch.

Best regards,
Rayagonda


On Mon, Sep 30, 2019 at 12:19 PM Rayagonda Kokatanur
<rayagonda.kokatanur@broadcom.com> wrote:
>
> From: Lori Hikichi <lori.hikichi@broadcom.com>
>
> Enable handling of i2c repeated start. The current code
> handles a multi msg i2c transfer as separate i2c bus
> transactions. This change will now handle this case
> using the i2c repeated start protocol. The number of msgs
> in a transfer is limited to two, and must be a write
> followed by a read.
>
> Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
> Signed-off-by: Ray Jui <ray.jui@broadcom.com>
> Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>
> ---
> changes from v1:
>  - Address following review comments from Wolfarm Sang,
>    Use i2c_8bit_addr_from_msg() api instead of decoding i2c_msg struct and
>    remove check against number of i2c message as it will be taken care
>    by core using quirks flags.
>
>  drivers/i2c/busses/i2c-bcm-iproc.c | 63 ++++++++++++++++++++++++++++++--------
>  1 file changed, 50 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> index d7fd76b..e478db7 100644
> --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -81,6 +81,7 @@
>  #define M_CMD_PROTOCOL_MASK          0xf
>  #define M_CMD_PROTOCOL_BLK_WR        0x7
>  #define M_CMD_PROTOCOL_BLK_RD        0x8
> +#define M_CMD_PROTOCOL_PROCESS       0xa
>  #define M_CMD_PEC_SHIFT              8
>  #define M_CMD_RD_CNT_SHIFT           0
>  #define M_CMD_RD_CNT_MASK            0xff
> @@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
>         return 0;
>  }
>
> -static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> -                                        struct i2c_msg *msg)
> +/*
> + * If 'process_call' is true, then this is a multi-msg transfer that requires
> + * a repeated start between the messages.
> + * More specifically, it must be a write (reg) followed by a read (data).
> + * The i2c quirks are set to enforce this rule.
> + */
> +static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
> +                                       struct i2c_msg *msgs, bool process_call)
>  {
>         int i;
>         u8 addr;
>         u32 val, tmp, val_intr_en;
>         unsigned int tx_bytes;
> +       struct i2c_msg *msg = &msgs[0];
>
>         /* check if bus is busy */
>         if (!!(iproc_i2c_rd_reg(iproc_i2c,
> @@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
>                         val = msg->buf[i];
>
>                         /* mark the last byte */
> -                       if (i == msg->len - 1)
> -                               val |= BIT(M_TX_WR_STATUS_SHIFT);
> +                       if (!process_call && (i == msg->len - 1))
> +                               val |= 1 << M_TX_WR_STATUS_SHIFT;
>
>                         iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
>                 }
>                 iproc_i2c->tx_bytes = tx_bytes;
>         }
>
> +       /* Process the read message if this is process call */
> +       if (process_call) {
> +               msg++;
> +               iproc_i2c->msg = msg;  /* point to second msg */
> +
> +               /*
> +                * The last byte to be sent out should be a slave
> +                * address with read operation
> +                */
> +               addr = i2c_8bit_addr_from_msg(msg);
> +               /* mark it the last byte out */
> +               val = addr | (1 << M_TX_WR_STATUS_SHIFT);
> +               iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
> +       }
> +
>         /* mark as incomplete before starting the transaction */
>         if (iproc_i2c->irq)
>                 reinit_completion(&iproc_i2c->done);
> @@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
>          * underrun interrupt, which will be triggerred when the TX FIFO is
>          * empty. When that happens we can then pump more data into the FIFO
>          */
> -       if (!(msg->flags & I2C_M_RD) &&
> +       if (!process_call && !(msg->flags & I2C_M_RD) &&
>             msg->len > iproc_i2c->tx_bytes)
>                 val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
>
> @@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
>          */
>         val = BIT(M_CMD_START_BUSY_SHIFT);
>         if (msg->flags & I2C_M_RD) {
> +               u32 protocol;
> +
>                 iproc_i2c->rx_bytes = 0;
>                 if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
>                         iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
> @@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
>                 /* enable the RX threshold interrupt */
>                 val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
>
> -               val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
> +               protocol = process_call ?
> +                               M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
> +
> +               val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
>                        (msg->len << M_CMD_RD_CNT_SHIFT);
>         } else {
>                 val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
> @@ -774,17 +802,24 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
>                               struct i2c_msg msgs[], int num)
>  {
>         struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
> -       int ret, i;
> +       bool process_call = false;
> +       int ret;
>
> -       /* go through all messages */
> -       for (i = 0; i < num; i++) {
> -               ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
> -               if (ret) {
> -                       dev_dbg(iproc_i2c->device, "xfer failed\n");
> -                       return ret;
> +       if (num == 2) {
> +               /* Repeated start, use process call */
> +               process_call = true;
> +               if (msgs[1].flags & I2C_M_NOSTART) {
> +                       dev_err(iproc_i2c->device, "Invalid repeated start\n");
> +                       return -EOPNOTSUPP;
>                 }
>         }
>
> +       ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
> +       if (ret) {
> +               dev_dbg(iproc_i2c->device, "xfer failed\n");
> +               return ret;
> +       }
> +
>         return num;
>  }
>
> @@ -806,6 +841,8 @@ static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
>  };
>
>  static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
> +       .flags = I2C_AQ_COMB_WRITE_THEN_READ,
> +       .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
>         .max_read_len = M_RX_MAX_READ_LEN,
>  };
>
> --
> 1.9.1
>
Rayagonda Kokatanur Oct. 17, 2019, 5:58 a.m. UTC | #2
Hi Wolfram,

Please review the patch and let me know if you still have any review comments.

Best regards,
Rayagonda

On Thu, Oct 10, 2019 at 3:02 PM Rayagonda Kokatanur
<rayagonda.kokatanur@broadcom.com> wrote:
>
> Hi Wolfram,
>
> Did you get a chance to review this patch.
>
> Best regards,
> Rayagonda
>
>
> On Mon, Sep 30, 2019 at 12:19 PM Rayagonda Kokatanur
> <rayagonda.kokatanur@broadcom.com> wrote:
> >
> > From: Lori Hikichi <lori.hikichi@broadcom.com>
> >
> > Enable handling of i2c repeated start. The current code
> > handles a multi msg i2c transfer as separate i2c bus
> > transactions. This change will now handle this case
> > using the i2c repeated start protocol. The number of msgs
> > in a transfer is limited to two, and must be a write
> > followed by a read.
> >
> > Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> > Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> > Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
> > Signed-off-by: Ray Jui <ray.jui@broadcom.com>
> > Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>
> > ---
> > changes from v1:
> >  - Address following review comments from Wolfarm Sang,
> >    Use i2c_8bit_addr_from_msg() api instead of decoding i2c_msg struct and
> >    remove check against number of i2c message as it will be taken care
> >    by core using quirks flags.
> >
> >  drivers/i2c/busses/i2c-bcm-iproc.c | 63 ++++++++++++++++++++++++++++++--------
> >  1 file changed, 50 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> > index d7fd76b..e478db7 100644
> > --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> > +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> > @@ -81,6 +81,7 @@
> >  #define M_CMD_PROTOCOL_MASK          0xf
> >  #define M_CMD_PROTOCOL_BLK_WR        0x7
> >  #define M_CMD_PROTOCOL_BLK_RD        0x8
> > +#define M_CMD_PROTOCOL_PROCESS       0xa
> >  #define M_CMD_PEC_SHIFT              8
> >  #define M_CMD_RD_CNT_SHIFT           0
> >  #define M_CMD_RD_CNT_MASK            0xff
> > @@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
> >         return 0;
> >  }
> >
> > -static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> > -                                        struct i2c_msg *msg)
> > +/*
> > + * If 'process_call' is true, then this is a multi-msg transfer that requires
> > + * a repeated start between the messages.
> > + * More specifically, it must be a write (reg) followed by a read (data).
> > + * The i2c quirks are set to enforce this rule.
> > + */
> > +static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
> > +                                       struct i2c_msg *msgs, bool process_call)
> >  {
> >         int i;
> >         u8 addr;
> >         u32 val, tmp, val_intr_en;
> >         unsigned int tx_bytes;
> > +       struct i2c_msg *msg = &msgs[0];
> >
> >         /* check if bus is busy */
> >         if (!!(iproc_i2c_rd_reg(iproc_i2c,
> > @@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> >                         val = msg->buf[i];
> >
> >                         /* mark the last byte */
> > -                       if (i == msg->len - 1)
> > -                               val |= BIT(M_TX_WR_STATUS_SHIFT);
> > +                       if (!process_call && (i == msg->len - 1))
> > +                               val |= 1 << M_TX_WR_STATUS_SHIFT;
> >
> >                         iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
> >                 }
> >                 iproc_i2c->tx_bytes = tx_bytes;
> >         }
> >
> > +       /* Process the read message if this is process call */
> > +       if (process_call) {
> > +               msg++;
> > +               iproc_i2c->msg = msg;  /* point to second msg */
> > +
> > +               /*
> > +                * The last byte to be sent out should be a slave
> > +                * address with read operation
> > +                */
> > +               addr = i2c_8bit_addr_from_msg(msg);
> > +               /* mark it the last byte out */
> > +               val = addr | (1 << M_TX_WR_STATUS_SHIFT);
> > +               iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
> > +       }
> > +
> >         /* mark as incomplete before starting the transaction */
> >         if (iproc_i2c->irq)
> >                 reinit_completion(&iproc_i2c->done);
> > @@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> >          * underrun interrupt, which will be triggerred when the TX FIFO is
> >          * empty. When that happens we can then pump more data into the FIFO
> >          */
> > -       if (!(msg->flags & I2C_M_RD) &&
> > +       if (!process_call && !(msg->flags & I2C_M_RD) &&
> >             msg->len > iproc_i2c->tx_bytes)
> >                 val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
> >
> > @@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> >          */
> >         val = BIT(M_CMD_START_BUSY_SHIFT);
> >         if (msg->flags & I2C_M_RD) {
> > +               u32 protocol;
> > +
> >                 iproc_i2c->rx_bytes = 0;
> >                 if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
> >                         iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
> > @@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> >                 /* enable the RX threshold interrupt */
> >                 val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
> >
> > -               val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
> > +               protocol = process_call ?
> > +                               M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
> > +
> > +               val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
> >                        (msg->len << M_CMD_RD_CNT_SHIFT);
> >         } else {
> >                 val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
> > @@ -774,17 +802,24 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
> >                               struct i2c_msg msgs[], int num)
> >  {
> >         struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
> > -       int ret, i;
> > +       bool process_call = false;
> > +       int ret;
> >
> > -       /* go through all messages */
> > -       for (i = 0; i < num; i++) {
> > -               ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
> > -               if (ret) {
> > -                       dev_dbg(iproc_i2c->device, "xfer failed\n");
> > -                       return ret;
> > +       if (num == 2) {
> > +               /* Repeated start, use process call */
> > +               process_call = true;
> > +               if (msgs[1].flags & I2C_M_NOSTART) {
> > +                       dev_err(iproc_i2c->device, "Invalid repeated start\n");
> > +                       return -EOPNOTSUPP;
> >                 }
> >         }
> >
> > +       ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
> > +       if (ret) {
> > +               dev_dbg(iproc_i2c->device, "xfer failed\n");
> > +               return ret;
> > +       }
> > +
> >         return num;
> >  }
> >
> > @@ -806,6 +841,8 @@ static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
> >  };
> >
> >  static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
> > +       .flags = I2C_AQ_COMB_WRITE_THEN_READ,
> > +       .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
> >         .max_read_len = M_RX_MAX_READ_LEN,
> >  };
> >
> > --
> > 1.9.1
> >
Wolfram Sang Oct. 24, 2019, 6:55 p.m. UTC | #3
On Mon, Sep 30, 2019 at 12:14:29PM +0530, Rayagonda Kokatanur wrote:
> From: Lori Hikichi <lori.hikichi@broadcom.com>
> 
> Enable handling of i2c repeated start. The current code
> handles a multi msg i2c transfer as separate i2c bus
> transactions. This change will now handle this case
> using the i2c repeated start protocol. The number of msgs
> in a transfer is limited to two, and must be a write
> followed by a read.
> 
> Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
> Signed-off-by: Ray Jui <ray.jui@broadcom.com>
> Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>

Patch looks good but doesn't apply for me on top of v5.4-rc4? What was
your base?

Also, I will apply it to for-next (v5.5). If you want it for v5.4, then
please add a Fixes tag.
Rayagonda Kokatanur Oct. 25, 2019, 4:51 a.m. UTC | #4
On Fri, Oct 25, 2019 at 12:25 AM Wolfram Sang <wsa@the-dreams.de> wrote:
>
> On Mon, Sep 30, 2019 at 12:14:29PM +0530, Rayagonda Kokatanur wrote:
> > From: Lori Hikichi <lori.hikichi@broadcom.com>
> >
> > Enable handling of i2c repeated start. The current code
> > handles a multi msg i2c transfer as separate i2c bus
> > transactions. This change will now handle this case
> > using the i2c repeated start protocol. The number of msgs
> > in a transfer is limited to two, and must be a write
> > followed by a read.
> >
> > Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> > Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> > Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
> > Signed-off-by: Ray Jui <ray.jui@broadcom.com>
> > Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>
>
> Patch looks good but doesn't apply for me on top of v5.4-rc4? What was
> your base?
>
> Also, I will apply it to for-next (v5.5). If you want it for v5.4, then
> please add a Fixes tag.
>
thank you, please apply it to for-next (v5.5).
Do I need to resend patch again for you to apply it to v5.5 ?

Patch
diff mbox series

diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index d7fd76b..e478db7 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -81,6 +81,7 @@ 
 #define M_CMD_PROTOCOL_MASK          0xf
 #define M_CMD_PROTOCOL_BLK_WR        0x7
 #define M_CMD_PROTOCOL_BLK_RD        0x8
+#define M_CMD_PROTOCOL_PROCESS       0xa
 #define M_CMD_PEC_SHIFT              8
 #define M_CMD_RD_CNT_SHIFT           0
 #define M_CMD_RD_CNT_MASK            0xff
@@ -675,13 +676,20 @@  static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
 	return 0;
 }
 
-static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
-					 struct i2c_msg *msg)
+/*
+ * If 'process_call' is true, then this is a multi-msg transfer that requires
+ * a repeated start between the messages.
+ * More specifically, it must be a write (reg) followed by a read (data).
+ * The i2c quirks are set to enforce this rule.
+ */
+static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
+					struct i2c_msg *msgs, bool process_call)
 {
 	int i;
 	u8 addr;
 	u32 val, tmp, val_intr_en;
 	unsigned int tx_bytes;
+	struct i2c_msg *msg = &msgs[0];
 
 	/* check if bus is busy */
 	if (!!(iproc_i2c_rd_reg(iproc_i2c,
@@ -707,14 +715,29 @@  static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 			val = msg->buf[i];
 
 			/* mark the last byte */
-			if (i == msg->len - 1)
-				val |= BIT(M_TX_WR_STATUS_SHIFT);
+			if (!process_call && (i == msg->len - 1))
+				val |= 1 << M_TX_WR_STATUS_SHIFT;
 
 			iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
 		}
 		iproc_i2c->tx_bytes = tx_bytes;
 	}
 
+	/* Process the read message if this is process call */
+	if (process_call) {
+		msg++;
+		iproc_i2c->msg = msg;  /* point to second msg */
+
+		/*
+		 * The last byte to be sent out should be a slave
+		 * address with read operation
+		 */
+		addr = i2c_8bit_addr_from_msg(msg);
+		/* mark it the last byte out */
+		val = addr | (1 << M_TX_WR_STATUS_SHIFT);
+		iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
+	}
+
 	/* mark as incomplete before starting the transaction */
 	if (iproc_i2c->irq)
 		reinit_completion(&iproc_i2c->done);
@@ -733,7 +756,7 @@  static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 	 * underrun interrupt, which will be triggerred when the TX FIFO is
 	 * empty. When that happens we can then pump more data into the FIFO
 	 */
-	if (!(msg->flags & I2C_M_RD) &&
+	if (!process_call && !(msg->flags & I2C_M_RD) &&
 	    msg->len > iproc_i2c->tx_bytes)
 		val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
 
@@ -743,6 +766,8 @@  static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 	 */
 	val = BIT(M_CMD_START_BUSY_SHIFT);
 	if (msg->flags & I2C_M_RD) {
+		u32 protocol;
+
 		iproc_i2c->rx_bytes = 0;
 		if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
 			iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
@@ -758,7 +783,10 @@  static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 		/* enable the RX threshold interrupt */
 		val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
 
-		val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
+		protocol = process_call ?
+				M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
+
+		val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
 		       (msg->len << M_CMD_RD_CNT_SHIFT);
 	} else {
 		val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
@@ -774,17 +802,24 @@  static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
 			      struct i2c_msg msgs[], int num)
 {
 	struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
-	int ret, i;
+	bool process_call = false;
+	int ret;
 
-	/* go through all messages */
-	for (i = 0; i < num; i++) {
-		ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
-		if (ret) {
-			dev_dbg(iproc_i2c->device, "xfer failed\n");
-			return ret;
+	if (num == 2) {
+		/* Repeated start, use process call */
+		process_call = true;
+		if (msgs[1].flags & I2C_M_NOSTART) {
+			dev_err(iproc_i2c->device, "Invalid repeated start\n");
+			return -EOPNOTSUPP;
 		}
 	}
 
+	ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
+	if (ret) {
+		dev_dbg(iproc_i2c->device, "xfer failed\n");
+		return ret;
+	}
+
 	return num;
 }
 
@@ -806,6 +841,8 @@  static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
 };
 
 static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
+	.flags = I2C_AQ_COMB_WRITE_THEN_READ,
+	.max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
 	.max_read_len = M_RX_MAX_READ_LEN,
 };