* [PATCH 0/2] i2c: Add I2C High-Speed mode support for qcom-geni
@ 2026-08-24 11:25 Jyothi Kumar Seerapu
2026-08-24 11:25 ` [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support Jyothi Kumar Seerapu
2026-08-24 11:25 ` [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode Jyothi Kumar Seerapu
0 siblings, 2 replies; 6+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-08-24 11:25 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Mukesh Kumar Savaliya, Viken Dadhaniya,
Andi Shyti
Cc: linux-arm-msm, dmaengine, linux-kernel, linux-i2c,
Jyothi Kumar Seerapu
Add I2C High-Speed (HS) mode support for the Qualcomm GENI I2C
controller, detecting HS mode requests based on a 3.4 MHz bus
frequency.
I2C HS mode is supported by the underlying QUP hardware starting with
QUPv3 core version 4.3 (per the GENI/QUP Hardware Programming Guide).
This series adds the driver support to detect that capability and
enable HS-mode transfers on SoCs whose QUP core version meets that
requirement, rather than tying HS mode to any specific peripheral.
Patch 1 (dmaengine: qcom-gpi) is for GPI DMA transfers and adds
a CONFIG1 TRE to the GPI DMA engine to convey HS-specific timing
parameters (tcycle_cnt/tlow_cnt) to the hardware.
Patch 2 (i2c: qcom-geni) handles FIFO/SE-DMA transfers; It configures
the GENI controller's source clock and timing accordingly, and adds
the HS-specific FIFO/SE-DMA opcodes and master-code preamble needed
to switch the bus from Fast Mode Plus to HS speed.
Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
Jyothi Kumar Seerapu (2):
dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
i2c: qcom-geni: Add support for I2C High-Speed mode
drivers/dma/qcom/gpi.c | 54 +++++++++++++--
drivers/i2c/busses/i2c-qcom-geni.c | 133 +++++++++++++++++++++++++++++++++----
include/linux/dma/qcom-gpi-dma.h | 28 +++++++-
3 files changed, 196 insertions(+), 19 deletions(-)
---
base-commit: 7079a12d7506b07fb53b54a664bfad5fa9b16d70
change-id: 20260821-i2c-hs-408768509865
Best regards,
--
Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
2026-08-24 11:25 [PATCH 0/2] i2c: Add I2C High-Speed mode support for qcom-geni Jyothi Kumar Seerapu
@ 2026-08-24 11:25 ` Jyothi Kumar Seerapu
2026-08-24 11:36 ` sashiko-bot
2026-08-24 11:25 ` [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode Jyothi Kumar Seerapu
1 sibling, 1 reply; 6+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-08-24 11:25 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Mukesh Kumar Savaliya, Viken Dadhaniya,
Andi Shyti
Cc: linux-arm-msm, dmaengine, linux-kernel, linux-i2c,
Jyothi Kumar Seerapu
Add support in the Qualcomm GPI (Generic Packet Interface) DMA engine
to configure I2C High-Speed (HS) mode transfers.
Introduce support for CONFIG1 Transfer Ring Element (TRE) to convey
HS-specific timing parameters to the hardware. Define a new
gpi_i2c_config1 structure containing tcycle_cnt and tlow_cnt fields,
with default values of 28 and 38 respectively, as required for 3.4 MHz
HS mode operation.
Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
drivers/dma/qcom/gpi.c | 54 +++++++++++++++++++++++++++++++++++-----
include/linux/dma/qcom-gpi-dma.h | 28 ++++++++++++++++++++-
2 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index a5055a6273af..52a7800ed4f5 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -21,6 +21,7 @@
#define TRE_TYPE_IMMEDIATE_DMA 0x11
#define TRE_TYPE_GO 0x20
#define TRE_TYPE_CONFIG0 0x22
+#define TRE_TYPE_CONFIG1 0x23
/* TRE flags */
#define TRE_FLAGS_CHAIN BIT(0)
@@ -58,9 +59,14 @@
#define TRE_I2C_C0_TX_PACK BIT(24)
#define TRE_I2C_C0_RX_PACK BIT(25)
+/* I2C Config1 WD0 */
+#define TRE_I2C_C1_TLOW GENMASK(9, 0)
+#define TRE_I2C_C1_TCYCLE GENMASK(19, 10)
+
/* I2C GO WD0 */
#define TRE_I2C_GO_CMD GENMASK(4, 0)
#define TRE_I2C_GO_ADDR GENMASK(14, 8)
+#define TRE_I2C_GO_MASTER_CODE GENMASK(17, 15)
#define TRE_I2C_GO_STRETCH BIT(26)
/* DMA TRE */
@@ -1631,7 +1637,7 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
struct gpi_tre *tre;
unsigned int i;
- /* first create config tre if applicable */
+ /* first create config0 tre if applicable */
if (i2c->set_config) {
tre = &desc->tre[tre_idx];
tre_idx++;
@@ -1645,20 +1651,48 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[1] = 0;
tre->dword[2] = u32_encode_bits(i2c->clk_div, TRE_C0_CLK_DIV);
+ tre->dword[2] |= u32_encode_bits(i2c->clk_src, TRE_C0_CLK_SRC);
tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE);
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
}
- /* create the GO tre for Tx */
- if (i2c->op == I2C_WRITE) {
+ /* Create CONFIG1 TRE if requested */
+ if (i2c->set_config1) {
tre = &desc->tre[tre_idx];
tre_idx++;
+ /* CONFIG1 TRE with timing parameters */
+ tre->dword[0] = u32_encode_bits(i2c->config1.tlow_cnt, TRE_I2C_C1_TLOW);
+ tre->dword[0] |= u32_encode_bits(i2c->config1.tcycle_cnt, TRE_I2C_C1_TCYCLE);
+ tre->dword[1] = 0;
+ tre->dword[2] = 0;
+ tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG1, TRE_FLAGS_TYPE);
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+ }
+
+ /* create the GO tre for Tx */
+ if (i2c->op == I2C_WRITE || i2c->op == I2C_HS_WRITE) {
+ u8 master_code = 0; /* Default master code for Linux EE */
+ u32 cmd_opcode;
+ bool is_hs_mode = false;
+
+ tre = &desc->tre[tre_idx];
+ tre_idx++;
+
+ is_hs_mode = (i2c->op == I2C_HS_WRITE || i2c->op == I2C_HS_READ);
+
+ /* Determine the command opcode based on HS mode and multi_msg flag */
if (i2c->multi_msg)
- tre->dword[0] = u32_encode_bits(I2C_READ, TRE_I2C_GO_CMD);
+ cmd_opcode = is_hs_mode ? I2C_HS_READ : I2C_READ;
else
- tre->dword[0] = u32_encode_bits(i2c->op, TRE_I2C_GO_CMD);
+ /* I2C HS write vs Regular I2C write */
+ cmd_opcode = is_hs_mode ? I2C_HS_WRITE : i2c->op;
+
+ tre->dword[0] = u32_encode_bits(cmd_opcode, TRE_I2C_GO_CMD);
+
+ if (is_hs_mode)
+ tre->dword[0] |= u32_encode_bits(master_code, TRE_I2C_GO_MASTER_CODE);
tre->dword[0] |= u32_encode_bits(i2c->addr, TRE_I2C_GO_ADDR);
tre->dword[0] |= u32_encode_bits(i2c->stretch, TRE_I2C_GO_STRETCH);
@@ -1674,7 +1708,7 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
}
- if (i2c->op == I2C_READ || i2c->multi_msg == false) {
+ if (i2c->op == I2C_READ || i2c->op == I2C_HS_READ || i2c->multi_msg == false) {
/* create the DMA TRE */
tre = &desc->tre[tre_idx];
tre_idx++;
@@ -1826,6 +1860,14 @@ gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (direction == DMA_DEV_TO_MEM) /* rx */
nr_tre = 1;
+ /* I2C High-Speed mode sends an extra CONFIG1 TRE ahead of the GO TRE */
+ if (gchan->protocol == QCOM_GPI_I2C) {
+ struct gpi_i2c_config *i2c = gchan->config;
+
+ if (i2c->set_config1)
+ nr_tre++;
+ }
+
/* calculate # of elements required & available */
nr = gpi_ring_num_elements_avail(ch_ring);
if (nr < nr_tre) {
diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h
index 332be28427e4..6e9f09fc109a 100644
--- a/include/linux/dma/qcom-gpi-dma.h
+++ b/include/linux/dma/qcom-gpi-dma.h
@@ -6,6 +6,24 @@
#ifndef QCOM_GPI_DMA_H
#define QCOM_GPI_DMA_H
+/**
+ * struct gpi_i2c_config1 - I2C High-Speed mode timing configuration
+ * @tcycle_cnt: I2C HS mode TCYCLE counter value
+ * @tlow_cnt: I2C HS mode TLOW counter value
+ *
+ * This structure is used for I2C High-Speed mode to configure the
+ * high-speed timing parameters via CONFIG1 TRE. The CONFIG1 TRE
+ * must be sent before the GO TRE when operating in HS mode.
+ *
+ * For I2C HS mode (3.4 MHz):
+ * - tcycle_cnt: Typically 28 for 100 MHz source clock
+ * - tlow_cnt: Typically 38 for 100 MHz source clock
+ */
+struct gpi_i2c_config1 {
+ u16 tcycle_cnt;
+ u16 tlow_cnt;
+};
+
/**
* enum spi_transfer_cmd - spi transfer commands
* @SPI_TX: SPI peripheral TX command
@@ -52,6 +70,8 @@ struct gpi_spi_config {
enum i2c_op {
I2C_WRITE = 1,
I2C_READ,
+ I2C_HS_WRITE = 0xa,
+ I2C_HS_READ = 0xb,
};
/**
@@ -62,15 +82,19 @@ enum i2c_op {
* @high_count: high period of clock
* @low_count: low period of clock
* @clk_div: source clock divider
+ * @clk_src: serial clock
* @addr: i2c bus address
* @stretch: stretch the clock at eot
- * @set_config: set peripheral config
+ * @set_config: set peripheral config (CONFIG0)
+ * @set_config1: set peripheral config1 (CONFIG1)
+ * @config1: I2C HS mode timing configuration (CONFIG1 TRE parameters)
* @rx_len: receive length for buffer
* @op: i2c cmd
* @multi_msg: is part of multi i2c r-w msgs
*/
struct gpi_i2c_config {
u8 set_config;
+ u8 set_config1;
u8 pack_enable;
u8 cycle_count;
u8 high_count;
@@ -78,6 +102,8 @@ struct gpi_i2c_config {
u8 addr;
u8 stretch;
u16 clk_div;
+ u32 clk_src;
+ struct gpi_i2c_config1 config1;
u32 rx_len;
enum i2c_op op;
bool multi_msg;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode
2026-08-24 11:25 [PATCH 0/2] i2c: Add I2C High-Speed mode support for qcom-geni Jyothi Kumar Seerapu
2026-08-24 11:25 ` [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support Jyothi Kumar Seerapu
@ 2026-08-24 11:25 ` Jyothi Kumar Seerapu
2026-08-24 11:39 ` sashiko-bot
1 sibling, 1 reply; 6+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-08-24 11:25 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Mukesh Kumar Savaliya, Viken Dadhaniya,
Andi Shyti
Cc: linux-arm-msm, dmaengine, linux-kernel, linux-i2c,
Jyothi Kumar Seerapu
Implement I2C High-Speed (HS) mode for the Qualcomm GENI I2C controller.
Detect HS mode requests based on 3.4 MHz frequency and configure the
hardware accordingly. When HS mode is active, set the source clock to
100 MHz and program timing parameters (TCYCLE=28, TLOW=38).
For FIFO/SE-DMA transfers, use HS-specific opcodes I2C_HS_WRITE (0xa)
and I2C_HS_READ (0xb). Transmit the master code at Fast Mode Plus
timing (1 MHz) before switching to 3.4 MHz for data transfer.
For GPI DMA, extend the configuration with a CONFIG1 TRE to pass HS
timing parameters to the DMA engine. Add gpi_i2c_config1 with
tcycle_cnt and tlow_cnt fields (defaults: 28 and 38).
Use the set_config1 flag in gpi_i2c_config to send CONFIG1 TRE before
the GO TRE in HS mode.
Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 133 +++++++++++++++++++++++++++++++++----
1 file changed, 121 insertions(+), 12 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 658636c1ee0e..3712e0dd9e68 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -21,9 +21,21 @@
#include <linux/spinlock.h>
#include <linux/units.h>
+#define SE_GENI_CFG_REG68 0x210
+#define SE_I2C_HS_TLOW_REG 0x268
#define SE_I2C_TX_TRANS_LEN 0x26c
#define SE_I2C_RX_TRANS_LEN 0x270
#define SE_I2C_SCL_COUNTERS 0x278
+#define SE_I2C_HS_TCYCLE_REG 0x27c
+
+/* I2C High-Speed mode definitions */
+#define I2C_HS_MODE_FREQ 3400000
+#define I2C_HS_SRC_CLK_FREQ (100 * HZ_PER_MHZ)
+#define I2C_HS_TCYCLE_CNT 28
+#define I2C_HS_TLOW_CNT 38
+/* I2C HS mode support requires QUPv3 core version >= 4.3 per HPG */
+#define QUP_I2C_HS_MIN_MAJOR 4
+#define QUP_I2C_HS_MIN_MINOR 3
#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
@@ -36,6 +48,9 @@
#define I2C_ADDR_ONLY 0x4
#define I2C_BUS_CLEAR 0x6
#define I2C_STOP_ON_BUS 0x7
+#define I2C_HS_WRITE 0xa
+#define I2C_HS_READ 0xb
+
/* M_CMD params for I2C */
#define PRE_CMD_DELAY BIT(0)
#define TIMESTAMP_BEFORE BIT(1)
@@ -138,6 +153,9 @@ struct geni_i2c_dev {
u32 num_msgs;
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
const struct geni_i2c_desc *dev_data;
+ bool is_hs_mode;
+ bool config1_sent;
+ u32 dfs_index;
};
struct geni_i2c_err_log {
@@ -162,9 +180,14 @@ static const struct geni_i2c_err_log gi2c_log[] = {
struct geni_i2c_clk_fld {
u32 clk_freq_out;
u8 clk_div;
- u8 t_high_cnt;
- u8 t_low_cnt;
- u8 t_cycle_cnt;
+ /*
+ * In normal mode, these counter values fit within 8 bits.
+ * In High-Speed mode, HS_TLOW_COUNT and HS_TCYCLE_COUNT are
+ * 10-bit fields, so u16 is required.
+ */
+ u16 t_high_cnt;
+ u16 t_low_cnt;
+ u16 t_cycle_cnt;
};
/*
@@ -194,10 +217,30 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
{}
};
+/* source_clock = 100 MHz */
+static const struct geni_i2c_clk_fld geni_i2c_clk_map_100mhz[] = {
+ { I2C_MAX_STANDARD_MODE_FREQ, 1, 449, 548, 998 },
+ { I2C_MAX_FAST_MODE_FREQ, 1, 76, 167, 248 },
+ { I2C_MAX_FAST_MODE_PLUS_FREQ, 1, 23, 59, 98 },
+ {}
+};
+
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
const struct geni_i2c_clk_fld *itr;
+ /* Check if HS mode is requested */
+ if (gi2c->clk_freq_out == I2C_HS_MODE_FREQ) {
+ gi2c->is_hs_mode = true;
+ /* For HS mode, source clock should be 100 MHz */
+ itr = geni_i2c_clk_map_100mhz;
+ /* For HS mode, start with 1MHz for master code */
+ gi2c->clk_fld = &itr[2];
+ return 0;
+ }
+
+ gi2c->is_hs_mode = false;
+
if (clk_get_rate(gi2c->se.clk) == 32 * HZ_PER_MHZ)
itr = geni_i2c_clk_map_32mhz;
else
@@ -219,7 +262,12 @@ static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
u32 val;
- writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
+ if (gi2c->is_hs_mode) {
+ writel_relaxed(I2C_HS_TCYCLE_CNT, gi2c->se.base + SE_I2C_HS_TCYCLE_REG);
+ writel_relaxed(I2C_HS_TLOW_CNT, gi2c->se.base + SE_I2C_HS_TLOW_REG);
+ }
+
+ writel_relaxed(gi2c->dfs_index, gi2c->se.base + SE_GENI_CLK_SEL);
val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
@@ -500,7 +548,11 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
geni_se_select_mode(se, GENI_SE_FIFO);
writel_relaxed(len, se->base + SE_I2C_RX_TRANS_LEN);
- geni_se_setup_m_cmd(se, I2C_READ, m_param);
+
+ if (gi2c->is_hs_mode)
+ geni_se_setup_m_cmd(se, I2C_HS_READ, m_param);
+ else
+ geni_se_setup_m_cmd(se, I2C_READ, m_param);
if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &rx_dma)) {
geni_se_select_mode(se, GENI_SE_FIFO);
@@ -539,7 +591,11 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
geni_se_select_mode(se, GENI_SE_FIFO);
writel_relaxed(len, se->base + SE_I2C_TX_TRANS_LEN);
- geni_se_setup_m_cmd(se, I2C_WRITE, m_param);
+
+ if (gi2c->is_hs_mode)
+ geni_se_setup_m_cmd(se, I2C_HS_WRITE, m_param);
+ else
+ geni_se_setup_m_cmd(se, I2C_WRITE, m_param);
if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &tx_dma)) {
geni_se_select_mode(se, GENI_SE_FIFO);
@@ -700,7 +756,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
goto out;
}
- if (op == I2C_WRITE)
+ if (op == I2C_WRITE || op == I2C_HS_WRITE)
map_dirn = DMA_TO_DEVICE;
else
map_dirn = DMA_FROM_DEVICE;
@@ -737,7 +793,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
peripheral->set_config = 0;
peripheral->multi_msg = true;
- if (op == I2C_WRITE)
+ if (op == I2C_WRITE || op == I2C_HS_WRITE)
dma_dirn = DMA_MEM_TO_DEV;
else
dma_dirn = DMA_DEV_TO_MEM;
@@ -827,6 +883,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
peripheral.high_count = itr->t_high_cnt;
peripheral.low_count = itr->t_low_cnt;
peripheral.clk_div = itr->clk_div;
+ peripheral.clk_src = gi2c->dfs_index;
peripheral.set_config = 1;
peripheral.multi_msg = false;
@@ -836,6 +893,13 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
gi2c->num_msgs = num;
gi2c->is_tx_multi_desc_xfer = false;
+ gi2c->config1_sent = false;
+
+ /* Initialize config1 TRE settings for HS mode */
+ if (gi2c->is_hs_mode) {
+ peripheral.config1.tcycle_cnt = I2C_HS_TCYCLE_CNT;
+ peripheral.config1.tlow_cnt = I2C_HS_TLOW_CNT;
+ }
tx_multi_xfer = &gi2c->i2c_multi_desc_config;
memset(tx_multi_xfer, 0, sizeof(struct geni_i2c_gpi_multi_desc_xfer));
@@ -883,14 +947,25 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
if (i > 0 && (!(msgs[i].flags & I2C_M_RD)))
peripheral.multi_msg = false;
- ret = geni_i2c_gpi(gi2c, msgs, &config,
- &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c);
+ /* Set config1 TRE only for HS mode */
+ if (gi2c->is_hs_mode && !gi2c->config1_sent) {
+ peripheral.set_config1 = 1;
+ gi2c->config1_sent = true;
+ } else {
+ peripheral.set_config1 = 0;
+ }
+
+ ret = geni_i2c_gpi(gi2c, msgs, &config, &tx_addr, &tx_buf,
+ gi2c->is_hs_mode ? I2C_HS_WRITE : I2C_WRITE, gi2c->tx_c);
if (ret)
goto err;
+ /* CONFIG1 TRE is only for the TX channel; clear before RX call */
+ peripheral.set_config1 = 0;
+
if (msgs[i].flags & I2C_M_RD) {
- ret = geni_i2c_gpi(gi2c, msgs, &config,
- &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c);
+ ret = geni_i2c_gpi(gi2c, msgs, &config, &rx_addr, &rx_buf,
+ gi2c->is_hs_mode ? I2C_HS_READ : I2C_READ, gi2c->rx_c);
if (ret)
goto err;
@@ -1037,6 +1112,7 @@ static int setup_gpi_dma(struct geni_i2c_dev *gi2c)
static int geni_i2c_init(struct geni_i2c_dev *gi2c)
{
u32 proto, tx_depth;
+ unsigned long freq_out;
bool fifo_disable;
int ret;
@@ -1046,6 +1122,39 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
return ret;
}
+ /*
+ * For I2C High-Speed mode, first verify QUP HW version supports it
+ * (requires QUPv3 core >= 4.3 per HPG), then configure 100 MHz source clock.
+ */
+ if (gi2c->is_hs_mode) {
+ u32 hw_ver = geni_se_get_qup_hw_version(&gi2c->se);
+ u32 major = GENI_SE_VERSION_MAJOR(hw_ver);
+ u32 minor = GENI_SE_VERSION_MINOR(hw_ver);
+
+ if (major < QUP_I2C_HS_MIN_MAJOR ||
+ (major == QUP_I2C_HS_MIN_MAJOR && minor < QUP_I2C_HS_MIN_MINOR)) {
+ dev_err(gi2c->se.dev,
+ "QUP HW v%u.%u does not support I2C HS mode (requires >= %u.%u)\n",
+ major, minor,
+ QUP_I2C_HS_MIN_MAJOR, QUP_I2C_HS_MIN_MINOR);
+ ret = -EOPNOTSUPP;
+ goto err;
+ }
+
+ ret = geni_se_clk_freq_match(&gi2c->se, I2C_HS_SRC_CLK_FREQ,
+ &gi2c->dfs_index, &freq_out, false);
+ if (ret) {
+ dev_err(gi2c->se.dev, "Failed to get DFS index for HS mode: %d\n", ret);
+ goto err;
+ }
+
+ ret = clk_set_rate(gi2c->se.clk, freq_out);
+ if (ret) {
+ dev_err(gi2c->se.dev, "Failed to set HS mode clock rate: %d\n", ret);
+ goto err;
+ }
+ }
+
proto = geni_se_read_proto(&gi2c->se);
if (proto == GENI_SE_INVALID_PROTO) {
ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C);
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
2026-08-24 11:25 ` [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support Jyothi Kumar Seerapu
@ 2026-08-24 11:36 ` sashiko-bot
2026-08-25 20:31 ` Frank Li
0 siblings, 1 reply; 6+ messages in thread
From: sashiko-bot @ 2026-08-24 11:36 UTC (permalink / raw)
To: Jyothi Kumar Seerapu; +Cc: vkoul, Frank.Li, dmaengine
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Heap Buffer Overflow and DMA Address Corruption in I2C High-Speed mode due to insufficient MAX_TRE
--
--- Patch [1]: [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support ---
commit 0c2b77de6fa90756115b4394bd0db3e62bed9d2a
Author: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
Add support in the Qualcomm GPI DMA engine to configure I2C High-Speed (HS)
mode transfers. This introduces support for the CONFIG1 Transfer Ring Element
(TRE) to convey HS-specific timing parameters.
> --- a/drivers/dma/qcom/gpi.c
> +++ b/drivers/dma/qcom/gpi.c
[ ... ]
> @@ -1631,7 +1637,7 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
> struct gpi_tre *tre;
> unsigned int i;
>
> - /* first create config tre if applicable */
> + /* first create config0 tre if applicable */
> if (i2c->set_config) {
> tre = &desc->tre[tre_idx];
> tre_idx++;
[ ... ]
> + /* Create CONFIG1 TRE if requested */
> + if (i2c->set_config1) {
> + tre = &desc->tre[tre_idx];
> + tre_idx++;
[ ... ]
> /* create the GO tre for Tx */
> - if (i2c->op == I2C_WRITE) {
> + if (i2c->op == I2C_WRITE || i2c->op == I2C_HS_WRITE) {
[ ... ]
> tre = &desc->tre[tre_idx];
> tre_idx++;
[ ... ]
> - if (i2c->op == I2C_READ || i2c->multi_msg == false) {
> + if (i2c->op == I2C_READ || i2c->op == I2C_HS_READ || i2c->multi_msg == false) {
> /* create the DMA TRE */
> tre = &desc->tre[tre_idx];
> tre_idx++;
>
> address = sg_dma_address(sgl);
> tre->dword[0] = lower_32_bits(address);
[Severity: High]
When I2C High-Speed mode is used with both set_config and set_config1,
can this cause an out-of-bounds write to the tre array?
Looking at struct gpi_desc, the tre array is statically sized by MAX_TRE,
which is defined as 3:
#define MAX_TRE 3
struct gpi_desc {
...
struct gpi_tre tre[MAX_TRE];
u32 num_tre;
};
If we generate CONFIG0, CONFIG1, GO, and DMA TREs, tre_idx will reach 3
here. Writing to desc->tre[3] would overflow the array and overwrite the
adjacent num_tre field.
Later, gpi_prep_slave_sg() sets gpi_desc->num_tre = 4, which would
overwrite the first 4 bytes of the newly written DMA TRE, potentially
causing an IOMMU fault or bus error when the hardware attempts the transfer.
Should MAX_TRE be increased to accommodate the new CONFIG1 TRE?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260824-i2c-hs-v1-0-8dd94e5c6113@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode
2026-08-24 11:25 ` [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode Jyothi Kumar Seerapu
@ 2026-08-24 11:39 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-08-24 11:39 UTC (permalink / raw)
To: Jyothi Kumar Seerapu; +Cc: vkoul, Frank.Li, dmaengine
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing I2C_HS_WRITE checks in geni_i2c_gpi() cause unintended TX DMA mapping for read messages and out-of-bounds array accesses.
--
--- Patch [2]: [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode ---
commit 010ffaa50c8bd9bb0e57a40b999e343cea742bb8
Author: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
i2c: qcom-geni: Add support for I2C High-Speed mode
Implement I2C High-Speed (HS) mode for the Qualcomm GENI I2C controller.
Detect HS mode requests based on 3.4 MHz frequency and configure the
hardware accordingly.
> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
> index 658636c1ee0e2..3712e0dd9e68d 100644
> --- a/drivers/i2c/busses/i2c-qcom-geni.c
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
[ ... ]
> @@ -700,7 +756,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
> goto out;
> }
>
> - if (op == I2C_WRITE)
> + if (op == I2C_WRITE || op == I2C_HS_WRITE)
> map_dirn = DMA_TO_DEVICE;
> else
> map_dirn = DMA_FROM_DEVICE;
[Severity: High]
Will this cause unintended TX DMA mapping and out-of-bounds array accesses
for High-Speed mode read messages?
It looks like op == I2C_HS_WRITE was added here for map_dirn, but it wasn't
added to the check that skips TX DMA mapping for read messages earlier in
this function:
if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) {
peripheral->multi_msg = true;
goto skip_tx_dma_map;
}
Because I2C_HS_WRITE isn't checked here, High-Speed reads will fail to skip
the TX DMA mapping.
Additionally, it appears the same omission happens later in the function
when incrementing msg_idx_cnt:
if (!((msgs[msg_idx].flags & I2C_M_RD) && op == I2C_WRITE))
gi2c_gpi_xfer->msg_idx_cnt++;
Since I2C_HS_WRITE is missing, the counter will be prematurely incremented
for HS reads. When the subsequent RX call processes the read message with
I2C_HS_READ, it will use the incremented msg_idx_cnt, potentially pointing
out of bounds of the msgs array.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260824-i2c-hs-v1-0-8dd94e5c6113@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
2026-08-24 11:36 ` sashiko-bot
@ 2026-08-25 20:31 ` Frank Li
0 siblings, 0 replies; 6+ messages in thread
From: Frank Li @ 2026-08-25 20:31 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Jyothi Kumar Seerapu, vkoul, Frank.Li, dmaengine
On Mon, Aug 24, 2026 at 11:36:52AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Heap Buffer Overflow and DMA Address Corruption in I2C High-Speed mode due to insufficient MAX_TRE
> --
>
> --- Patch [1]: [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support ---
> commit 0c2b77de6fa90756115b4394bd0db3e62bed9d2a
> Author: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
>
> dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support
>
> Add support in the Qualcomm GPI DMA engine to configure I2C High-Speed (HS)
> mode transfers. This introduces support for the CONFIG1 Transfer Ring Element
> (TRE) to convey HS-specific timing parameters.
>
> > --- a/drivers/dma/qcom/gpi.c
> > +++ b/drivers/dma/qcom/gpi.c
> [ ... ]
> > @@ -1631,7 +1637,7 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
> > struct gpi_tre *tre;
> > unsigned int i;
> >
> > - /* first create config tre if applicable */
> > + /* first create config0 tre if applicable */
> > if (i2c->set_config) {
> > tre = &desc->tre[tre_idx];
> > tre_idx++;
> [ ... ]
> > + /* Create CONFIG1 TRE if requested */
> > + if (i2c->set_config1) {
> > + tre = &desc->tre[tre_idx];
> > + tre_idx++;
> [ ... ]
> > /* create the GO tre for Tx */
> > - if (i2c->op == I2C_WRITE) {
> > + if (i2c->op == I2C_WRITE || i2c->op == I2C_HS_WRITE) {
> [ ... ]
> > tre = &desc->tre[tre_idx];
> > tre_idx++;
> [ ... ]
> > - if (i2c->op == I2C_READ || i2c->multi_msg == false) {
> > + if (i2c->op == I2C_READ || i2c->op == I2C_HS_READ || i2c->multi_msg == false) {
> > /* create the DMA TRE */
> > tre = &desc->tre[tre_idx];
> > tre_idx++;
> >
> > address = sg_dma_address(sgl);
> > tre->dword[0] = lower_32_bits(address);
>
> [Severity: High]
> When I2C High-Speed mode is used with both set_config and set_config1,
> can this cause an out-of-bounds write to the tre array?
>
> Looking at struct gpi_desc, the tre array is statically sized by MAX_TRE,
> which is defined as 3:
>
> #define MAX_TRE 3
>
> struct gpi_desc {
> ...
> struct gpi_tre tre[MAX_TRE];
> u32 num_tre;
> };
>
> If we generate CONFIG0, CONFIG1, GO, and DMA TREs, tre_idx will reach 3
> here. Writing to desc->tre[3] would overflow the array and overwrite the
> adjacent num_tre field.
>
> Later, gpi_prep_slave_sg() sets gpi_desc->num_tre = 4, which would
> overwrite the first 4 bytes of the newly written DMA TRE, potentially
> causing an IOMMU fault or bus error when the hardware attempts the transfer.
>
> Should MAX_TRE be increased to accommodate the new CONFIG1 TRE?
Please check this one, look like make sense.
Frank
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260824-i2c-hs-v1-0-8dd94e5c6113@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-25 20:31 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24 11:25 [PATCH 0/2] i2c: Add I2C High-Speed mode support for qcom-geni Jyothi Kumar Seerapu
2026-08-24 11:25 ` [PATCH 1/2] dmaengine: qcom-gpi: Add I2C High-Speed mode configuration support Jyothi Kumar Seerapu
2026-08-24 11:36 ` sashiko-bot
2026-08-25 20:31 ` Frank Li
2026-08-24 11:25 ` [PATCH 2/2] i2c: qcom-geni: Add support for I2C High-Speed mode Jyothi Kumar Seerapu
2026-08-24 11:39 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox