Message ID | 20241015120750.21217-1-quic_jseerapu@quicinc.com |
---|---|
Headers | show |
Series | Add Block event interrupt support for I2C protocol | expand |
On 15/10/2024 14:07, Jyothi Kumar Seerapu wrote: > When high performance with multiple i2c messages in a single transfer > is required, employ Block Event Interrupt (BEI) to trigger interrupts > after specific messages transfer and the last message transfer, > thereby reducing interrupts. > > For each i2c message transfer, a series of Transfer Request Elements(TREs) > must be programmed, including config tre for frequency configuration, > go tre for holding i2c address and dma tre for holding dma buffer address, > length as per the hardware programming guide. For transfer using BEI, > multiple I2C messages may necessitate the preparation of config, go, > and tx DMA TREs. However, a channel TRE size of 64 is often insufficient, > potentially leading to failures due to inadequate memory space. Please kindly test the patches before you sent them. Upstream is not a testing service. Best regards, Krzysztof
On 15-10-24, 17:37, Jyothi Kumar Seerapu wrote: > When high performance with multiple i2c messages in a single transfer > is required, employ Block Event Interrupt (BEI) to trigger interrupts > after specific messages transfer and the last message transfer, > thereby reducing interrupts. > > For each i2c message transfer, a series of Transfer Request Elements(TREs) > must be programmed, including config tre for frequency configuration, > go tre for holding i2c address and dma tre for holding dma buffer address, > length as per the hardware programming guide. For transfer using BEI, > multiple I2C messages may necessitate the preparation of config, go, > and tx DMA TREs. However, a channel TRE size of 64 is often insufficient, > potentially leading to failures due to inadequate memory space. > > Add additional argument to dma-cell property for channel TRE size. > With this, adjust the channel TRE size via the device tree. > The default size is 64, but clients can modify this value based on > their specific requirements. > > Signed-off-by: Jyothi Kumar Seerapu <quic_jseerapu@quicinc.com> > --- > Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 6 ++++-- > 1 file changed, 4 insertions(+), 2 deletions(-) > > diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml > index 4df4e61895d2..002495921643 100644 > --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml > +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml > @@ -54,14 +54,16 @@ properties: > maxItems: 13 > > "#dma-cells": > - const: 3 > + minItems: 3 > + maxItems: 4 > description: > > DMA clients must use the format described in dma.txt, giving a phandle > - to the DMA controller plus the following 3 integer cells: > + to the DMA controller plus the following 4 integer cells: > - channel: if set to 0xffffffff, any available channel will be allocated > for the client. Otherwise, the exact channel specified will be used. > - seid: serial id of the client as defined in the SoC documentation. > - client: type of the client as defined in dt-bindings/dma/qcom-gpi.h > + - channel-tre-size: size of the channel TRE (transfer ring element) This is a firmware /software property, why should this be in hardware description?
Hi Jyothi, ... > @@ -523,26 +576,49 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, > enum dma_transfer_direction dma_dirn; > struct dma_async_tx_descriptor *desc; > int ret; > + struct gpi_multi_xfer *gi2c_gpi_xfer; > + dma_cookie_t cookie; > > peripheral = config->peripheral_config; > - > - dma_buf = i2c_get_dma_safe_msg_buf(msg, 1); > - if (!dma_buf) > + gi2c_gpi_xfer = &peripheral->multi_xfer; > + gi2c_gpi_xfer->msg_idx_cnt = cur_msg_idx; > + dma_buf = gi2c_gpi_xfer->dma_buf[gi2c_gpi_xfer->buf_idx]; > + addr = gi2c_gpi_xfer->dma_addr[gi2c_gpi_xfer->buf_idx]; > + > + dma_buf = i2c_get_dma_safe_msg_buf(&msgs[gi2c_gpi_xfer->msg_idx_cnt], 1); > + if (!dma_buf) { > + gi2c->err = -ENOMEM; > return -ENOMEM; > + } > > if (op == I2C_WRITE) > map_dirn = DMA_TO_DEVICE; > else > map_dirn = DMA_FROM_DEVICE; > > - addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn); > + addr = dma_map_single(gi2c->se.dev->parent, > + dma_buf, msgs[gi2c_gpi_xfer->msg_idx_cnt].len, You could save msgs[gi2c_gpi_xfer->msg_idx_cnt] in a separate variable to avoid this extra indexing. > + map_dirn); > if (dma_mapping_error(gi2c->se.dev->parent, addr)) { > - i2c_put_dma_safe_msg_buf(dma_buf, msg, false); > + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[gi2c_gpi_xfer->msg_idx_cnt], > + false); > + gi2c->err = -ENOMEM; > return -ENOMEM; > } > > + if (gi2c->is_tx_multi_xfer) { > + if (((gi2c_gpi_xfer->msg_idx_cnt + 1) % NUM_MSGS_PER_IRQ)) > + peripheral->flags |= QCOM_GPI_BLOCK_EVENT_IRQ; > + else > + peripheral->flags &= ~QCOM_GPI_BLOCK_EVENT_IRQ; > + > + /* BEI bit to be cleared for last TRE */ > + if (gi2c_gpi_xfer->msg_idx_cnt == gi2c->num_msgs - 1) > + peripheral->flags &= ~QCOM_GPI_BLOCK_EVENT_IRQ; > + } > + > /* set the length as message for rx txn */ > - peripheral->rx_len = msg->len; > + peripheral->rx_len = msgs[gi2c_gpi_xfer->msg_idx_cnt].len; > peripheral->op = op; > > ret = dmaengine_slave_config(dma_chan, config); > @@ -560,25 +636,56 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, > else > dma_dirn = DMA_DEV_TO_MEM; > > - desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags); > + desc = dmaengine_prep_slave_single(dma_chan, addr, > + msgs[gi2c_gpi_xfer->msg_idx_cnt].len, > + dma_dirn, flags); > if (!desc) { > dev_err(gi2c->se.dev, "prep_slave_sg failed\n"); > - ret = -EIO; > + gi2c->err = -EIO; > goto err_config; > } > > desc->callback_result = i2c_gpi_cb_result; > desc->callback_param = gi2c; > > - dmaengine_submit(desc); > - *buf = dma_buf; > - *dma_addr_p = addr; > + if (!((msgs[cur_msg_idx].flags & I2C_M_RD) && op == I2C_WRITE)) { > + gi2c_gpi_xfer->msg_idx_cnt++; > + gi2c_gpi_xfer->buf_idx = (cur_msg_idx + 1) % QCOM_GPI_MAX_NUM_MSGS; > + } > + cookie = dmaengine_submit(desc); > + if (dma_submit_error(cookie)) { > + dev_err(gi2c->se.dev, > + "%s: dmaengine_submit failed (%d)\n", __func__, cookie); > + return -EINVAL; goto err_config? > + } > > + if (gi2c->is_tx_multi_xfer) { > + dma_async_issue_pending(gi2c->tx_c); > + if ((cur_msg_idx == (gi2c->num_msgs - 1)) || > + (gi2c_gpi_xfer->msg_idx_cnt >= > + QCOM_GPI_MAX_NUM_MSGS + gi2c_gpi_xfer->freed_msg_cnt)) { > + ret = gpi_multi_desc_process(gi2c->se.dev, gi2c_gpi_xfer, > + gi2c->num_msgs, XFER_TIMEOUT, > + &gi2c->done); > + if (ret) { > + dev_dbg(gi2c->se.dev, > + "I2C multi write msg transfer timeout: %d\n", > + ret); if you are returning an error, then print an error. > + gi2c->err = -ETIMEDOUT; gi2c->err = ret? > + goto err_config; > + } > + } > + } else { > + /* Non multi descriptor message transfer */ > + *buf = dma_buf; > + *dma_addr_p = addr; > + } > return 0; > > err_config: > - dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn); > - i2c_put_dma_safe_msg_buf(dma_buf, msg, false); > + dma_unmap_single(gi2c->se.dev->parent, addr, > + msgs[cur_msg_idx].len, map_dirn); > + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[cur_msg_idx], false); > return ret; I would have one more label here: out: gi2c->err = ret; return ret; in order to avoid always assigning twice > } > > @@ -590,6 +697,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i > unsigned long time_left; > dma_addr_t tx_addr, rx_addr; > void *tx_buf = NULL, *rx_buf = NULL; > + struct gpi_multi_xfer *tx_multi_xfer; > const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; > > config.peripheral_config = &peripheral; > @@ -603,6 +711,39 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i > peripheral.set_config = 1; > peripheral.multi_msg = false; > > + gi2c->gpi_config = &peripheral; > + gi2c->num_msgs = num; > + gi2c->is_tx_multi_xfer = false; > + gi2c->tx_irq_cnt = 0; > + > + tx_multi_xfer = &peripheral.multi_xfer; > + tx_multi_xfer->msg_idx_cnt = 0; > + tx_multi_xfer->buf_idx = 0; > + tx_multi_xfer->unmap_msg_cnt = 0; > + tx_multi_xfer->freed_msg_cnt = 0; > + tx_multi_xfer->irq_msg_cnt = 0; > + tx_multi_xfer->irq_cnt = 0; you can initialize tx_multi_xfer to "{ };" to avoid all these " = 0" > + > + /* > + * If number of write messages are four and higher then > + * configure hardware for multi descriptor transfers with BEI. > + */ > + if (num >= MIN_NUM_OF_MSGS_MULTI_DESC) { > + gi2c->is_tx_multi_xfer = true; > + for (i = 0; i < num; i++) { > + if (msgs[i].flags & I2C_M_RD) { > + /* > + * Multi descriptor transfer with BEI > + * support is enabled for write transfers. > + * Add BEI optimization support for read > + * transfers later. > + */ > + gi2c->is_tx_multi_xfer = false; > + break; > + } > + } > + } > + > for (i = 0; i < num; i++) { > gi2c->cur = &msgs[i]; > gi2c->err = 0; > @@ -613,14 +754,16 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i > peripheral.stretch = 1; > > peripheral.addr = msgs[i].addr; > + if (i > 0 && (!(msgs[i].flags & I2C_M_RD))) > + peripheral.multi_msg = false; > > - ret = geni_i2c_gpi(gi2c, &msgs[i], &config, > + ret = geni_i2c_gpi(gi2c, msgs, i, &config, what is the point of passing 'i' if you always refer to msgs[i] in geni_i2c_gpi()? > &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c); > if (ret) > goto err; > > if (msgs[i].flags & I2C_M_RD) { > - ret = geni_i2c_gpi(gi2c, &msgs[i], &config, > + ret = geni_i2c_gpi(gi2c, msgs, i, &config, > &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c); > if (ret) > goto err; > @@ -628,18 +771,28 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i > dma_async_issue_pending(gi2c->rx_c); > } > > - dma_async_issue_pending(gi2c->tx_c); > - > - time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); > - if (!time_left) > - gi2c->err = -ETIMEDOUT; > + if (!gi2c->is_tx_multi_xfer) { > + dma_async_issue_pending(gi2c->tx_c); > + time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); > + if (!time_left) { > + dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__); > + gi2c->err = -ETIMEDOUT; > + } > + } > > if (gi2c->err) { > ret = gi2c->err; > goto err; > } > > - geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); > + if (!gi2c->is_tx_multi_xfer) { > + geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); > + } else { > + if (gi2c->tx_irq_cnt != tx_multi_xfer->irq_cnt) { else if (...) { ... } Andi
On 10/15/2024 7:01 PM, Krzysztof Kozlowski wrote: > On 15/10/2024 14:07, Jyothi Kumar Seerapu wrote: >> When high performance with multiple i2c messages in a single transfer >> is required, employ Block Event Interrupt (BEI) to trigger interrupts >> after specific messages transfer and the last message transfer, >> thereby reducing interrupts. >> >> For each i2c message transfer, a series of Transfer Request Elements(TREs) >> must be programmed, including config tre for frequency configuration, >> go tre for holding i2c address and dma tre for holding dma buffer address, >> length as per the hardware programming guide. For transfer using BEI, >> multiple I2C messages may necessitate the preparation of config, go, >> and tx DMA TREs. However, a channel TRE size of 64 is often insufficient, >> potentially leading to failures due to inadequate memory space. > Please kindly test the patches before you sent them. Upstream is not a > testing service. Sure, i will take care to test the required patches. > > Best regards, > Krzysztof >
On 10/16/2024 10:24 AM, Vinod Koul wrote: > On 15-10-24, 17:37, Jyothi Kumar Seerapu wrote: >> When high performance with multiple i2c messages in a single transfer >> is required, employ Block Event Interrupt (BEI) to trigger interrupts >> after specific messages transfer and the last message transfer, >> thereby reducing interrupts. >> >> For each i2c message transfer, a series of Transfer Request Elements(TREs) >> must be programmed, including config tre for frequency configuration, >> go tre for holding i2c address and dma tre for holding dma buffer address, >> length as per the hardware programming guide. For transfer using BEI, >> multiple I2C messages may necessitate the preparation of config, go, >> and tx DMA TREs. However, a channel TRE size of 64 is often insufficient, >> potentially leading to failures due to inadequate memory space. >> >> Add additional argument to dma-cell property for channel TRE size. >> With this, adjust the channel TRE size via the device tree. >> The default size is 64, but clients can modify this value based on >> their specific requirements. >> >> Signed-off-by: Jyothi Kumar Seerapu <quic_jseerapu@quicinc.com> >> --- >> Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 6 ++++-- >> 1 file changed, 4 insertions(+), 2 deletions(-) >> >> diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> index 4df4e61895d2..002495921643 100644 >> --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> @@ -54,14 +54,16 @@ properties: >> maxItems: 13 >> >> "#dma-cells": >> - const: 3 >> + minItems: 3 >> + maxItems: 4 >> description: > >> DMA clients must use the format described in dma.txt, giving a phandle >> - to the DMA controller plus the following 3 integer cells: >> + to the DMA controller plus the following 4 integer cells: >> - channel: if set to 0xffffffff, any available channel will be allocated >> for the client. Otherwise, the exact channel specified will be used. >> - seid: serial id of the client as defined in the SoC documentation. >> - client: type of the client as defined in dt-bindings/dma/qcom-gpi.h >> + - channel-tre-size: size of the channel TRE (transfer ring element) > This is a firmware /software property, why should this be in hardware > description? This is a software property and here trying to add channel tre size as a 4th argument of dma-cells property. In V2, i have reverted the DT and binding changes related to adding new argument for dma-cells property and used GPI driver defined value. Regards, JyothiKumar >
On 10/16/2024 10:24 AM, Vinod Koul wrote: > On 15-10-24, 17:37, Jyothi Kumar Seerapu wrote: >> When high performance with multiple i2c messages in a single transfer >> is required, employ Block Event Interrupt (BEI) to trigger interrupts >> after specific messages transfer and the last message transfer, >> thereby reducing interrupts. >> >> For each i2c message transfer, a series of Transfer Request Elements(TREs) >> must be programmed, including config tre for frequency configuration, >> go tre for holding i2c address and dma tre for holding dma buffer address, >> length as per the hardware programming guide. For transfer using BEI, >> multiple I2C messages may necessitate the preparation of config, go, >> and tx DMA TREs. However, a channel TRE size of 64 is often insufficient, >> potentially leading to failures due to inadequate memory space. >> >> Add additional argument to dma-cell property for channel TRE size. >> With this, adjust the channel TRE size via the device tree. >> The default size is 64, but clients can modify this value based on >> their specific requirements. >> >> Signed-off-by: Jyothi Kumar Seerapu <quic_jseerapu@quicinc.com> >> --- >> Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 6 ++++-- >> 1 file changed, 4 insertions(+), 2 deletions(-) >> >> diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> index 4df4e61895d2..002495921643 100644 >> --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml >> @@ -54,14 +54,16 @@ properties: >> maxItems: 13 >> >> "#dma-cells": >> - const: 3 >> + minItems: 3 >> + maxItems: 4 >> description: > >> DMA clients must use the format described in dma.txt, giving a phandle >> - to the DMA controller plus the following 3 integer cells: >> + to the DMA controller plus the following 4 integer cells: >> - channel: if set to 0xffffffff, any available channel will be allocated >> for the client. Otherwise, the exact channel specified will be used. >> - seid: serial id of the client as defined in the SoC documentation. >> - client: type of the client as defined in dt-bindings/dma/qcom-gpi.h >> + - channel-tre-size: size of the channel TRE (transfer ring element) > > This is a firmware /software property, why should this be in hardware > description? > Hi, Yes, this is a software property and added as a 4th argument of "dma-cells" for configuring channel tre size, so added the description for dma-cells. In V2 patch, i reverted the changes and will handle with the default channel tre size present in the GPI driver. Regards, JyothiKumar
On 10/16/2024 8:36 PM, Andi Shyti wrote: > Hi Jyothi, > > ... > >> @@ -523,26 +576,49 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, >> enum dma_transfer_direction dma_dirn; >> struct dma_async_tx_descriptor *desc; >> int ret; >> + struct gpi_multi_xfer *gi2c_gpi_xfer; >> + dma_cookie_t cookie; >> >> peripheral = config->peripheral_config; >> - >> - dma_buf = i2c_get_dma_safe_msg_buf(msg, 1); >> - if (!dma_buf) >> + gi2c_gpi_xfer = &peripheral->multi_xfer; >> + gi2c_gpi_xfer->msg_idx_cnt = cur_msg_idx; >> + dma_buf = gi2c_gpi_xfer->dma_buf[gi2c_gpi_xfer->buf_idx]; >> + addr = gi2c_gpi_xfer->dma_addr[gi2c_gpi_xfer->buf_idx]; >> + >> + dma_buf = i2c_get_dma_safe_msg_buf(&msgs[gi2c_gpi_xfer->msg_idx_cnt], 1); >> + if (!dma_buf) { >> + gi2c->err = -ENOMEM; >> return -ENOMEM; >> + } >> >> if (op == I2C_WRITE) >> map_dirn = DMA_TO_DEVICE; >> else >> map_dirn = DMA_FROM_DEVICE; >> >> - addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn); >> + addr = dma_map_single(gi2c->se.dev->parent, >> + dma_buf, msgs[gi2c_gpi_xfer->msg_idx_cnt].len, > > You could save msgs[gi2c_gpi_xfer->msg_idx_cnt] in a separate > variable to avoid this extra indexing. > Thanks Andi, moved gi2c_gpi_xfer->msg_idx_cnt to separate local variable. >> + map_dirn); >> if (dma_mapping_error(gi2c->se.dev->parent, addr)) { >> - i2c_put_dma_safe_msg_buf(dma_buf, msg, false); >> + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[gi2c_gpi_xfer->msg_idx_cnt], >> + false); >> + gi2c->err = -ENOMEM; >> return -ENOMEM; >> } >> >> + if (gi2c->is_tx_multi_xfer) { >> + if (((gi2c_gpi_xfer->msg_idx_cnt + 1) % NUM_MSGS_PER_IRQ)) >> + peripheral->flags |= QCOM_GPI_BLOCK_EVENT_IRQ; >> + else >> + peripheral->flags &= ~QCOM_GPI_BLOCK_EVENT_IRQ; >> + >> + /* BEI bit to be cleared for last TRE */ >> + if (gi2c_gpi_xfer->msg_idx_cnt == gi2c->num_msgs - 1) >> + peripheral->flags &= ~QCOM_GPI_BLOCK_EVENT_IRQ; >> + } >> + >> /* set the length as message for rx txn */ >> - peripheral->rx_len = msg->len; >> + peripheral->rx_len = msgs[gi2c_gpi_xfer->msg_idx_cnt].len; >> peripheral->op = op; >> >> ret = dmaengine_slave_config(dma_chan, config); >> @@ -560,25 +636,56 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, >> else >> dma_dirn = DMA_DEV_TO_MEM; >> >> - desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags); >> + desc = dmaengine_prep_slave_single(dma_chan, addr, >> + msgs[gi2c_gpi_xfer->msg_idx_cnt].len, >> + dma_dirn, flags); >> if (!desc) { >> dev_err(gi2c->se.dev, "prep_slave_sg failed\n"); >> - ret = -EIO; >> + gi2c->err = -EIO; >> goto err_config; >> } >> >> desc->callback_result = i2c_gpi_cb_result; >> desc->callback_param = gi2c; >> >> - dmaengine_submit(desc); >> - *buf = dma_buf; >> - *dma_addr_p = addr; >> + if (!((msgs[cur_msg_idx].flags & I2C_M_RD) && op == I2C_WRITE)) { >> + gi2c_gpi_xfer->msg_idx_cnt++; >> + gi2c_gpi_xfer->buf_idx = (cur_msg_idx + 1) % QCOM_GPI_MAX_NUM_MSGS; >> + } >> + cookie = dmaengine_submit(desc); >> + if (dma_submit_error(cookie)) { >> + dev_err(gi2c->se.dev, >> + "%s: dmaengine_submit failed (%d)\n", __func__, cookie); >> + return -EINVAL; > > goto err_config? yes, updated it. > >> + } >> >> + if (gi2c->is_tx_multi_xfer) { >> + dma_async_issue_pending(gi2c->tx_c); >> + if ((cur_msg_idx == (gi2c->num_msgs - 1)) || >> + (gi2c_gpi_xfer->msg_idx_cnt >= >> + QCOM_GPI_MAX_NUM_MSGS + gi2c_gpi_xfer->freed_msg_cnt)) { >> + ret = gpi_multi_desc_process(gi2c->se.dev, gi2c_gpi_xfer, >> + gi2c->num_msgs, XFER_TIMEOUT, >> + &gi2c->done); >> + if (ret) { >> + dev_dbg(gi2c->se.dev, >> + "I2C multi write msg transfer timeout: %d\n", >> + ret); > > if you are returning an error, then print an error. sure, updated it to error in V2 patch. > >> + gi2c->err = -ETIMEDOUT; > > gi2c->err = ret? Yes in this case, ret is -ETIMEDOUT, so updated in V2 patch as gi2c->err= ret. > >> + goto err_config; >> + } >> + } >> + } else { >> + /* Non multi descriptor message transfer */ >> + *buf = dma_buf; >> + *dma_addr_p = addr; >> + } >> return 0; >> >> err_config: >> - dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn); >> - i2c_put_dma_safe_msg_buf(dma_buf, msg, false); >> + dma_unmap_single(gi2c->se.dev->parent, addr, >> + msgs[cur_msg_idx].len, map_dirn); >> + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[cur_msg_idx], false); >> return ret; > > I would have one more label here: > > out: > gi2c->err = ret; > > return ret; > > in order to avoid always assigning twice Thanks, added new label as out and handled it. > >> } >> >> @@ -590,6 +697,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i >> unsigned long time_left; >> dma_addr_t tx_addr, rx_addr; >> void *tx_buf = NULL, *rx_buf = NULL; >> + struct gpi_multi_xfer *tx_multi_xfer; >> const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; >> >> config.peripheral_config = &peripheral; >> @@ -603,6 +711,39 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i >> peripheral.set_config = 1; >> peripheral.multi_msg = false; >> >> + gi2c->gpi_config = &peripheral; >> + gi2c->num_msgs = num; >> + gi2c->is_tx_multi_xfer = false; >> + gi2c->tx_irq_cnt = 0; >> + >> + tx_multi_xfer = &peripheral.multi_xfer; >> + tx_multi_xfer->msg_idx_cnt = 0; >> + tx_multi_xfer->buf_idx = 0; >> + tx_multi_xfer->unmap_msg_cnt = 0; >> + tx_multi_xfer->freed_msg_cnt = 0; >> + tx_multi_xfer->irq_msg_cnt = 0; >> + tx_multi_xfer->irq_cnt = 0; > > you can initialize tx_multi_xfer to "{ };" to avoid all these > " = 0" Sure, done memset tx_multi_xfer to 0 in V2 patch. > >> + >> + /* >> + * If number of write messages are four and higher then >> + * configure hardware for multi descriptor transfers with BEI. >> + */ >> + if (num >= MIN_NUM_OF_MSGS_MULTI_DESC) { >> + gi2c->is_tx_multi_xfer = true; >> + for (i = 0; i < num; i++) { >> + if (msgs[i].flags & I2C_M_RD) { >> + /* >> + * Multi descriptor transfer with BEI >> + * support is enabled for write transfers. >> + * Add BEI optimization support for read >> + * transfers later. >> + */ >> + gi2c->is_tx_multi_xfer = false; >> + break; >> + } >> + } >> + } >> + >> for (i = 0; i < num; i++) { >> gi2c->cur = &msgs[i]; >> gi2c->err = 0; >> @@ -613,14 +754,16 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i >> peripheral.stretch = 1; >> >> peripheral.addr = msgs[i].addr; >> + if (i > 0 && (!(msgs[i].flags & I2C_M_RD))) >> + peripheral.multi_msg = false; >> >> - ret = geni_i2c_gpi(gi2c, &msgs[i], &config, >> + ret = geni_i2c_gpi(gi2c, msgs, i, &config, > > what is the point of passing 'i' if you always refer to msgs[i] > in geni_i2c_gpi()? Handled with new variable in "geni_i2c_gpi "and so no need to pass current i2c msg index, removed it in V2 patch. > >> &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c); >> if (ret) >> goto err; >> >> if (msgs[i].flags & I2C_M_RD) { >> - ret = geni_i2c_gpi(gi2c, &msgs[i], &config, >> + ret = geni_i2c_gpi(gi2c, msgs, i, &config, >> &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c); >> if (ret) >> goto err; >> @@ -628,18 +771,28 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i >> dma_async_issue_pending(gi2c->rx_c); >> } >> >> - dma_async_issue_pending(gi2c->tx_c); >> - >> - time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); >> - if (!time_left) >> - gi2c->err = -ETIMEDOUT; >> + if (!gi2c->is_tx_multi_xfer) { >> + dma_async_issue_pending(gi2c->tx_c); >> + time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); >> + if (!time_left) { >> + dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__); >> + gi2c->err = -ETIMEDOUT; >> + } >> + } >> >> if (gi2c->err) { >> ret = gi2c->err; >> goto err; >> } >> >> - geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); >> + if (!gi2c->is_tx_multi_xfer) { >> + geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); >> + } else { >> + if (gi2c->tx_irq_cnt != tx_multi_xfer->irq_cnt) { > > else if (...) { > ... > } Sure, else if used here in V2 patch. > > Andi Regards, JyothiKumar