diff options
| -rw-r--r-- | drivers/i2c/busses/i2c-qcom-geni.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index a4acb78fafb6..a482a4c60744 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -625,8 +625,8 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], { struct gpi_i2c_config *peripheral; unsigned int flags; - void *dma_buf; - dma_addr_t addr; + void *dma_buf = NULL; + dma_addr_t addr = 0; enum dma_data_direction map_dirn; enum dma_transfer_direction dma_dirn; struct dma_async_tx_descriptor *desc; @@ -639,6 +639,16 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config; msg_idx = gi2c_gpi_xfer->msg_idx_cnt; + /* + * Skip TX DMA mapping for a read message (I2C_M_RD) to avoid + * programming an extra TX DMA TRE that would cause an unintended + * write cycle on the I2C bus before the actual read operation. + */ + if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) { + peripheral->multi_msg = true; + goto skip_tx_dma_map; + } + dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1); if (!dma_buf) { ret = -ENOMEM; @@ -658,6 +668,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], goto out; } +skip_tx_dma_map: if (gi2c->is_tx_multi_desc_xfer) { flags = DMA_CTRL_ACK; @@ -740,9 +751,12 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], return 0; err_config: - dma_unmap_single(gi2c->se.dev->parent, addr, - msgs[msg_idx].len, map_dirn); - i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); + /* Avoid DMA unmap as the write operation skipped DMA mapping */ + if (dma_buf) { + dma_unmap_single(gi2c->se.dev->parent, addr, + msgs[msg_idx].len, map_dirn); + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); + } out: gi2c->err = ret; |
