Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i2c: qcom-geni: Avoid extra TX DMA TRE for single read message in GPI mode

In GPI mode, the I2C GENI driver programs an extra TX DMA transfer
descriptor (TRE) on the TX channel when handling a single read message.
This results in an unintended write phase being issued on the I2C bus,
even though a read transaction does not require any TX data.

For a single-byte read, the correct hardware sequence consists of the
CONFIG and GO commands followed by a single RX DMA TRE. Programming an
additional TX DMA TRE is redundant, causes unnecessary DMA buffer
mapping on the TX channel, and may lead to incorrect bus behavior.

Update the transfer logic to avoid programming a TX DMA TRE for single
read messages in GPI mode.

Co-developed-by: Maramaina Naresh <naresh.maramaina@oss.qualcomm.com>
Signed-off-by: Maramaina Naresh <naresh.maramaina@oss.qualcomm.com>
Signed-off-by: Aniket Randive <aniket.randive@oss.qualcomm.com>
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260410101949.2315058-1-aniket.randive@oss.qualcomm.com

authored by

Aniket Randive and committed by
Andi Shyti
656147fb 6ecea208

+19 -5
+19 -5
drivers/i2c/busses/i2c-qcom-geni.c
··· 625 625 { 626 626 struct gpi_i2c_config *peripheral; 627 627 unsigned int flags; 628 - void *dma_buf; 629 - dma_addr_t addr; 628 + void *dma_buf = NULL; 629 + dma_addr_t addr = 0; 630 630 enum dma_data_direction map_dirn; 631 631 enum dma_transfer_direction dma_dirn; 632 632 struct dma_async_tx_descriptor *desc; ··· 638 638 peripheral = config->peripheral_config; 639 639 gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config; 640 640 msg_idx = gi2c_gpi_xfer->msg_idx_cnt; 641 + 642 + /* 643 + * Skip TX DMA mapping for a read message (I2C_M_RD) to avoid 644 + * programming an extra TX DMA TRE that would cause an unintended 645 + * write cycle on the I2C bus before the actual read operation. 646 + */ 647 + if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) { 648 + peripheral->multi_msg = true; 649 + goto skip_tx_dma_map; 650 + } 641 651 642 652 dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1); 643 653 if (!dma_buf) { ··· 668 658 goto out; 669 659 } 670 660 661 + skip_tx_dma_map: 671 662 if (gi2c->is_tx_multi_desc_xfer) { 672 663 flags = DMA_CTRL_ACK; 673 664 ··· 751 740 return 0; 752 741 753 742 err_config: 754 - dma_unmap_single(gi2c->se.dev->parent, addr, 755 - msgs[msg_idx].len, map_dirn); 756 - i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); 743 + /* Avoid DMA unmap as the write operation skipped DMA mapping */ 744 + if (dma_buf) { 745 + dma_unmap_single(gi2c->se.dev->parent, addr, 746 + msgs[msg_idx].len, map_dirn); 747 + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); 748 + } 757 749 758 750 out: 759 751 gi2c->err = ret;