* [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support
2026-08-13 14:00 [PATCH v9 0/4] Enable multi-owner I2C support for QCOM GENI controllers Mukesh Kumar Savaliya
@ 2026-08-13 14:00 ` Mukesh Kumar Savaliya
2026-08-13 14:10 ` sashiko-bot
2026-08-13 14:00 ` [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers Mukesh Kumar Savaliya
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Mukesh Kumar Savaliya @ 2026-08-13 14:00 UTC (permalink / raw)
To: viken.dadhaniya, andi.shyti, robh, krzk+dt, conor+dt, vkoul,
Frank.Li, andersson, konradybcio, dmitry.baryshkov, linmq006,
zhengxingda, kees, agross, linux-arm-msm, linux-i2c, devicetree,
linux-kernel, dmaengine
Cc: krzysztof.kozlowski, bartosz.golaszewski, bjorn.andersson,
konrad.dybcio, brgl, Mukesh Kumar Savaliya
Document a DeviceTree property to describe QUP-based I2C controllers that
are shared with one or more other system processors.
On some Qualcomm platforms, a QUP-based I2C controller may be accessed by
multiple system processors (for example, APPS and DSP). In such
configurations, the operating system must not assume exclusive ownership
of the controller or its associated hardware resources.
The new qcom,qup-multi-owner property indicates that the controller is
externally shared and that the operating system must avoid operations
which rely on sole control of the hardware.
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
---
.../bindings/i2c/qcom,i2c-geni-qcom.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
index 51534953a69c..ed9b029603fd 100644
--- a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
+++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
@@ -60,6 +60,22 @@ properties:
power-domains:
maxItems: 1
+ qcom,qup-multi-owner:
+ type: boolean
+ description:
+ Indicates that the QUP-based controller is shared with one or more
+ other system processors and must not be assumed to have exclusive
+ ownership by the operating system.
+
+ The associated GPIOs must not be reconfigured into a sleep state
+ during runtime suspend, as doing so may disrupt transactions
+ initiated by another owner of the controller.
+
+ Each owner is responsible for maintaining any resource votes
+ required for operation of the shared controller (for example clocks,
+ power domains, interconnect bandwidth, or other platform-specific
+ resources)
+
reg:
maxItems: 1
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support
2026-08-13 14:00 ` [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support Mukesh Kumar Savaliya
@ 2026-08-13 14:10 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-08-13 14:10 UTC (permalink / raw)
To: Mukesh Kumar Savaliya
Cc: vkoul, Frank.Li, devicetree, robh, dmaengine, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The schema documents a runtime restriction for `qcom,qup-multi-owner` but fails to enforce it programmatically, allowing invalid device trees to pass validation.
--
--- Patch [1]: [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support ---
commit 744011d8e769def492430680cc665ca844b312d6
Author: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support
This commit documents a new qcom,qup-multi-owner property for QUP-based I2C
controllers that are shared with other system processors. It instructs the
operating system to avoid assuming exclusive ownership of the hardware.
> diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
> index 51534953a69cf..ed9b029603fdf 100644
> --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
> +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
[ ... ]
> @@ -60,6 +60,22 @@ properties:
> power-domains:
> maxItems: 1
>
> + qcom,qup-multi-owner:
> + type: boolean
> + description:
> + Indicates that the QUP-based controller is shared with one or more
> + other system processors and must not be assumed to have exclusive
> + ownership by the operating system.
> +
> + The associated GPIOs must not be reconfigured into a sleep state
> + during runtime suspend, as doing so may disrupt transactions
> + initiated by another owner of the controller.
[Severity: Medium]
The prose correctly documents that the GPIOs must not be put into a sleep
state when this property is present, but should this be enforced
programmatically in the schema?
Without an allOf conditional block restricting pinctrl-names to maxItems: 1
and disallowing pinctrl-1, device trees that mistakenly provide a sleep
pinctrl state alongside qcom,qup-multi-owner will silently pass
dt_binding_check and dtbs_check.
At runtime, the power management core would then automatically apply the sleep
state during runtime suspend, which disrupts the hardware as warned against in
the description.
> +
> + Each owner is responsible for maintaining any resource votes
> + required for operation of the shared controller (for example clocks,
> + power domains, interconnect bandwidth, or other platform-specific
> + resources)
>
> reg:
> maxItems: 1
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260813140047.1006478-1-mukesh.savaliya@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers
2026-08-13 14:00 [PATCH v9 0/4] Enable multi-owner I2C support for QCOM GENI controllers Mukesh Kumar Savaliya
2026-08-13 14:00 ` [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support Mukesh Kumar Savaliya
@ 2026-08-13 14:00 ` Mukesh Kumar Savaliya
2026-08-13 14:19 ` sashiko-bot
2026-08-13 14:00 ` [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers Mukesh Kumar Savaliya
2026-08-13 14:00 ` [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode Mukesh Kumar Savaliya
3 siblings, 1 reply; 9+ messages in thread
From: Mukesh Kumar Savaliya @ 2026-08-13 14:00 UTC (permalink / raw)
To: viken.dadhaniya, andi.shyti, robh, krzk+dt, conor+dt, vkoul,
Frank.Li, andersson, konradybcio, dmitry.baryshkov, linmq006,
zhengxingda, kees, agross, linux-arm-msm, linux-i2c, devicetree,
linux-kernel, dmaengine
Cc: krzysztof.kozlowski, bartosz.golaszewski, bjorn.andersson,
konrad.dybcio, brgl, Mukesh Kumar Savaliya
Some platforms use a QUP-based I2C controller in a configuration where the
controller is shared with another system processor (described in DT using
qcom,qup-multi-owner). In such setups, GPI hardware lock/unlock TREs can be
used to serialize access to the controller.
Add support to emit lock and unlock TREs around I2C transfers and increase
the maximum TRE count to account for the additional elements.
The GPI driver determines lock/unlock TRE placement autonomously based on
transaction boundaries, with no guidance required from the DMA client:
- A LOCK TRE is inserted before the first transfer in a transaction. The
driver detects the start of a new transaction by checking that no lock
is currently held (lock_pending == false).
- An UNLOCK TRE is inserted after the final write transfer in a
transaction. The DMA_PREP_INTERRUPT flag on the descriptor marks the
last descriptor in the batch.
The multi-owner flag is communicated once via the initial dmaengine_slave_
config call (set_config == 1) through the multi_owner field of struct
gpi_i2c_config, and is latched in gchan->multi_owner for the lifetime of
the transfer sequence. This keeps the locking mechanism as an internal GPI
implementation detail, consistent with the BAM-DMA approach, so DMA
consumers remain unaware of the underlying locking hardware.
Signed-off-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
---
drivers/dma/qcom/gpi.c | 77 +++++++++++++++++++++++++++++++-
include/linux/dma/qcom-gpi-dma.h | 4 ++
2 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index a5055a6273af..25fe7410bee2 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020, Linaro Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <dt-bindings/dma/qcom-gpi.h>
@@ -67,6 +68,14 @@
#define TRE_DMA_LEN GENMASK(23, 0)
#define TRE_DMA_IMMEDIATE_LEN GENMASK(3, 0)
+/* Lock TRE */
+#define TRE_LOCK BIT(0)
+#define TRE_MINOR_TYPE GENMASK(19, 16)
+#define TRE_MAJOR_TYPE GENMASK(23, 20)
+
+/* Unlock TRE */
+#define TRE_UNLOCK BIT(8)
+
/* Register offsets from gpi-top */
#define GPII_n_CH_k_CNTXT_0_OFFS(n, k) (0x20000 + (0x4000 * (n)) + (0x80 * (k)))
#define GPII_n_CH_k_CNTXT_0_EL_SIZE GENMASK(31, 24)
@@ -492,6 +501,8 @@ struct gchan {
u32 dir;
struct gpi_ring ch_ring;
void *config;
+ bool multi_owner; /* controller is shared; insert lock/unlock TREs */
+ bool lock_pending; /* LOCK TRE has been inserted, UNLOCK not yet emitted */
};
struct gpii {
@@ -518,7 +529,7 @@ struct gpii {
bool ieob_set;
};
-#define MAX_TRE 3
+#define MAX_TRE 5
struct gpi_desc {
struct virt_dma_desc vd;
@@ -1605,6 +1616,7 @@ static int
gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
{
struct gchan *gchan = to_gchan(chan);
+ struct gpi_i2c_config *i2c_cfg;
void *new_config;
if (!config->peripheral_config)
@@ -1617,6 +1629,17 @@ gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
gchan->config = new_config;
memcpy(gchan->config, config->peripheral_config, config->peripheral_size);
+ /*
+ * Latch the multi_owner flag from the initial config call so the GPI
+ * driver can autonomously insert LOCK/UNLOCK TREs without the client
+ * having to track transfer boundaries.
+ */
+ if (gchan->protocol == QCOM_GPI_I2C) {
+ i2c_cfg = gchan->config;
+ if (i2c_cfg->set_config)
+ gchan->multi_owner = i2c_cfg->multi_owner;
+ }
+
return 0;
}
@@ -1627,10 +1650,33 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
struct gpi_i2c_config *i2c = chan->config;
struct device *dev = chan->gpii->gpi_dev->dev;
unsigned int tre_idx = 0;
+ bool is_last = !!(flags & DMA_PREP_INTERRUPT);
dma_addr_t address;
struct gpi_tre *tre;
unsigned int i;
+ /*
+ * Insert a LOCK TRE before the first transfer of a multi-owner
+ * transaction. The GPI driver detects the transaction start
+ * autonomously: if multi_owner is set and no lock has been issued
+ * since the last unlock (lock_pending == false), this is the first
+ * descriptor in a new transaction.
+ */
+ if (chan->multi_owner && !chan->lock_pending) {
+ tre = &desc->tre[tre_idx];
+ tre_idx++;
+
+ tre->dword[0] = 0;
+ tre->dword[1] = 0;
+ tre->dword[2] = 0;
+ tre->dword[3] = u32_encode_bits(1, TRE_LOCK);
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
+ tre->dword[3] |= u32_encode_bits(0, TRE_MINOR_TYPE);
+ tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
+
+ chan->lock_pending = true;
+ }
+
/* first create config tre if applicable */
if (i2c->set_config) {
tre = &desc->tre[tre_idx];
@@ -1690,6 +1736,35 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
if (!(flags & DMA_PREP_INTERRUPT))
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_BEI);
+
+ /*
+ * Chain the DMA TRE to the UNLOCK TRE when this is the last
+ * descriptor in the transaction (DMA_PREP_INTERRUPT set) and
+ * the channel is in multi-owner mode.
+ */
+ if (chan->multi_owner && is_last && i2c->op != I2C_READ)
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+ }
+
+ /*
+ * Insert an UNLOCK TRE after the last write transfer of a multi-owner
+ * transaction. DMA_PREP_INTERRUPT marks the final descriptor in the
+ * batch; reads carry their own completion event so the unlock follows
+ * the write leg of each read message.
+ */
+ if (chan->multi_owner && is_last && i2c->op != I2C_READ) {
+ tre = &desc->tre[tre_idx];
+ tre_idx++;
+
+ tre->dword[0] = 0;
+ tre->dword[1] = 0;
+ tre->dword[2] = 0;
+ tre->dword[3] = u32_encode_bits(1, TRE_UNLOCK);
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
+ tre->dword[3] |= u32_encode_bits(1, TRE_MINOR_TYPE);
+ tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
+
+ chan->lock_pending = false;
}
for (i = 0; i < tre_idx; i++)
diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h
index 332be28427e4..590ca7935f2b 100644
--- a/include/linux/dma/qcom-gpi-dma.h
+++ b/include/linux/dma/qcom-gpi-dma.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, Linaro Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_GPI_DMA_H
@@ -68,6 +69,8 @@ enum i2c_op {
* @rx_len: receive length for buffer
* @op: i2c cmd
* @multi_msg: is part of multi i2c r-w msgs
+ * @multi_owner: controller is shared with another system processor;
+ * the GPI driver will insert lock/unlock TREs automatically
*/
struct gpi_i2c_config {
u8 set_config;
@@ -81,6 +84,7 @@ struct gpi_i2c_config {
u32 rx_len;
enum i2c_op op;
bool multi_msg;
+ bool multi_owner;
};
#endif /* QCOM_GPI_DMA_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers
2026-08-13 14:00 ` [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers Mukesh Kumar Savaliya
@ 2026-08-13 14:19 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-08-13 14:19 UTC (permalink / raw)
To: Mukesh Kumar Savaliya
Cc: robh, dmaengine, devicetree, vkoul, Frank.Li, conor+dt
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Critical] Setting `TRE_FLAGS_IEOB` on LOCK and UNLOCK TREs causes multiple completion events per descriptor, leading to Use-After-Free and queue corruption.
- [High] The driver prematurely drops the hardware lock in Write-then-Read multi-message transfers due to misinterpreting `DMA_PREP_INTERRUPT`.
- [High] Failure to reset `gchan->lock_pending` during channel reset or termination permanently bypasses hardware locking for subsequent transactions.
- [High] `gpi_prep_slave_sg()` blindly hardcodes the maximum TRE requirement to 3, failing to account for the newly added LOCK and UNLOCK TREs, causing ring buffer overflow.
Pre-existing issues:
- [High] Dangling pointer in `gchan->config` leads to Use-After-Free during channel reallocation.
--
--- Patch [2]: [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers ---
commit b6883d50c999f402ed4f7be119614262119be22d
Author: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers
This commit adds support for emitting lock and unlock Transfer Ring Elements
(TREs) around I2C transfers on QUP-based controllers shared with other
processors. The GPI driver autonomously determines the placement of these
TREs based on transaction boundaries and latches a multi-owner flag during
initial configuration.
> diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
> index a5055a6273af6..25fe7410bee2e 100644
> --- a/drivers/dma/qcom/gpi.c
> +++ b/drivers/dma/qcom/gpi.c
[ ... ]
> @@ -518,7 +529,7 @@ struct gpii {
> bool ieob_set;
> };
>
> -#define MAX_TRE 3
> +#define MAX_TRE 5
[Severity: High]
While MAX_TRE is increased to 5 here, does gpi_prep_slave_sg() still
hardcode the maximum TRE requirement to 3 when checking if the ring buffer
has enough elements available via gpi_ring_num_elements_avail()?
If the ring buffer only has 3 or 4 slots available, could this pass the
check but fail to enqueue the remaining elements like the UNLOCK TRE,
potentially locking the hardware bus?
> struct gpi_desc {
[ ... ]
> @@ -1617,6 +1629,17 @@ gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
> gchan->config = new_config;
> memcpy(gchan->config, config->peripheral_config, config->peripheral_size);
>
[Severity: High]
This isn't a bug introduced by this patch, but when
gpi_free_chan_resources() frees this memory with kfree(gchan->config), it
leaves gchan->config as a dangling pointer.
Since gchan elements are statically allocated arrays reused across DMA
channel requests, if the channel is requested again, will this call to
krealloc() receive the freed pointer and cause a slab use-after-free?
> + /*
> + * Latch the multi_owner flag from the initial config call so the GPI
[ ... ]
> @@ -1627,10 +1650,33 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
> + /*
> + * Insert a LOCK TRE before the first transfer of a multi-owner
> + * transaction. The GPI driver detects the transaction start
> + * autonomously: if multi_owner is set and no lock has been issued
> + * since the last unlock (lock_pending == false), this is the first
> + * descriptor in a new transaction.
> + */
> + if (chan->multi_owner && !chan->lock_pending) {
> + tre = &desc->tre[tre_idx];
> + tre_idx++;
> +
> + tre->dword[0] = 0;
> + tre->dword[1] = 0;
> + tre->dword[2] = 0;
> + tre->dword[3] = u32_encode_bits(1, TRE_LOCK);
> + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
[Severity: Critical]
Adding TRE_FLAGS_IEOB to the LOCK and UNLOCK TREs means a single transaction
produces an EOB event (from Lock), an EOT event (from DMA), and another EOB
event (from Unlock).
In gpi_process_xfer_compl_event(), EOB events fall through to the success
path which pops the active descriptor from gchan->vc and frees it. Since a
single descriptor generates multiple events, could the event handler pop and
free unrelated descriptors from the queue, causing a use-after-free?
> + tre->dword[3] |= u32_encode_bits(0, TRE_MINOR_TYPE);
> + tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
> +
> + chan->lock_pending = true;
[Severity: High]
If a transaction errors out after a LOCK TRE is generated and the client
calls gpi_terminate_all() to abort the transfer, gchan->lock_pending is not
reset to false.
On the next transaction, will gpi_create_i2c_tre() evaluate lock_pending as
true and skip inserting the necessary LOCK TRE, causing transactions to run
on the shared bus without acquiring the hardware lock?
> + }
> +
> /* first create config tre if applicable */
> if (i2c->set_config) {
[ ... ]
> @@ -1690,6 +1736,35 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
[ ... ]
> + /*
> + * Insert an UNLOCK TRE after the last write transfer of a multi-owner
> + * transaction. DMA_PREP_INTERRUPT marks the final descriptor in the
> + * batch; reads carry their own completion event so the unlock follows
> + * the write leg of each read message.
> + */
> + if (chan->multi_owner && is_last && i2c->op != I2C_READ) {
> + tre = &desc->tre[tre_idx];
> + tre_idx++;
> +
> + tre->dword[0] = 0;
> + tre->dword[1] = 0;
> + tre->dword[2] = 0;
> + tre->dword[3] = u32_encode_bits(1, TRE_UNLOCK);
[Severity: High]
If the i2c-qcom-geni client driver executes Write-then-Read sequences by
setting DMA_PREP_INTERRUPT on the write leg to wait for its completion, will
this logic equate the flag with the absolute end of the I2C transaction?
Could this prematurely append an UNLOCK TRE between the write and read,
destroying the I2C bus stretch and allowing other owners to interleave
transactions and corrupt the bus state?
> + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
> + tre->dword[3] |= u32_encode_bits(1, TRE_MINOR_TYPE);
> + tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
> +
> + chan->lock_pending = false;
> }
>
> for (i = 0; i < tre_idx; i++)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260813140047.1006478-1-mukesh.savaliya@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers
2026-08-13 14:00 [PATCH v9 0/4] Enable multi-owner I2C support for QCOM GENI controllers Mukesh Kumar Savaliya
2026-08-13 14:00 ` [PATCH v9 1/4] dt-bindings: i2c: qcom,i2c-geni: Document multi-owner controller support Mukesh Kumar Savaliya
2026-08-13 14:00 ` [PATCH v9 2/4] dmaengine: qcom: gpi: Add lock/unlock TREs for multi-owner I2C transfers Mukesh Kumar Savaliya
@ 2026-08-13 14:00 ` Mukesh Kumar Savaliya
2026-08-13 14:17 ` sashiko-bot
2026-08-13 14:00 ` [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode Mukesh Kumar Savaliya
3 siblings, 1 reply; 9+ messages in thread
From: Mukesh Kumar Savaliya @ 2026-08-13 14:00 UTC (permalink / raw)
To: viken.dadhaniya, andi.shyti, robh, krzk+dt, conor+dt, vkoul,
Frank.Li, andersson, konradybcio, dmitry.baryshkov, linmq006,
zhengxingda, kees, agross, linux-arm-msm, linux-i2c, devicetree,
linux-kernel, dmaengine
Cc: krzysztof.kozlowski, bartosz.golaszewski, bjorn.andersson,
konrad.dybcio, brgl, Mukesh Kumar Savaliya
On platforms where a GENI Serial Engine is shared with another system
processor, selecting the "sleep" pinctrl state can disrupt ongoing
transfers initiated by the other processor.
Teach geni_se_resources_off() to skip selecting the pinctrl sleep state
when the Serial Engine is marked as shared, while still allowing the
rest of the resource shutdown sequence to proceed.
This is required for multi-owner configurations (described via DeviceTree
with qcom,qup-multi-owner on the protocol controller node).
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
---
drivers/soc/qcom/qcom-geni-se.c | 14 +++++++++++---
include/linux/soc/qcom/geni-se.h | 2 ++
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 873bfbd6b2b7..a4da75d54d6c 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -610,9 +610,17 @@ int geni_se_resources_off(struct geni_se *se)
if (has_acpi_companion(se->dev))
return 0;
- ret = pinctrl_pm_select_sleep_state(se->dev);
- if (ret)
- return ret;
+ /*
+ * Select the "sleep" pinctrl state only when the serial engine is
+ * exclusively owned by this system processor. For shared controller
+ * configurations, another system processor may still be using the pins,
+ * and switching them to "sleep" can disrupt ongoing transfers.
+ */
+ if (!se->multi_owner) {
+ ret = pinctrl_pm_select_sleep_state(se->dev);
+ if (ret)
+ return ret;
+ }
geni_se_clks_off(se);
return 0;
diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index 29a53bbc0dd4..725f89a44551 100644
--- a/include/linux/soc/qcom/geni-se.h
+++ b/include/linux/soc/qcom/geni-se.h
@@ -66,6 +66,7 @@ struct geni_icc_path {
* @icc_paths: Array of ICC paths for SE
* @pd_list: Power domain list for managing power domains
* @has_opp: Indicates if OPP is supported
+ * @multi_owner: True if SE is shared between multiple owners.
*/
struct geni_se {
void __iomem *base;
@@ -78,6 +79,7 @@ struct geni_se {
struct geni_icc_path icc_paths[3];
struct dev_pm_domain_list *pd_list;
bool has_opp;
+ bool multi_owner;
};
/* Common SE registers */
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers
2026-08-13 14:00 ` [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers Mukesh Kumar Savaliya
@ 2026-08-13 14:17 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-08-13 14:17 UTC (permalink / raw)
To: Mukesh Kumar Savaliya
Cc: vkoul, dmaengine, conor+dt, Frank.Li, devicetree, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The `qcom,qup-multi-owner` DeviceTree property is never parsed, making the `multi_owner` feature completely non-functional.
- [High] The runtime PM suspend callback `geni_se_resources_deactivate()` lacks the `se->multi_owner` check, breaking multi-owner controller operation during runtime suspend.
- [High] The GPI DMA driver prematurely releases the hardware lock during multi-message I2C read transactions, destroying bus atomicity.
--
--- Patch [3]: [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers ---
commit ceae3d1ac31371a041b92e87207badc3969f7e87
Author: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers
On platforms where a GENI Serial Engine is shared with another system
processor, selecting the "sleep" pinctrl state can disrupt ongoing
transfers initiated by the other processor. This patch skips selecting
the sleep state when the engine is marked as shared.
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> index 873bfbd6b2b7b..a4da75d54d6c3 100644
> --- a/drivers/soc/qcom/qcom-geni-se.c
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -610,9 +610,17 @@ int geni_se_resources_off(struct geni_se *se)
> if (has_acpi_companion(se->dev))
> return 0;
>
> - ret = pinctrl_pm_select_sleep_state(se->dev);
> - if (ret)
> - return ret;
> + /*
> + * Select the "sleep" pinctrl state only when the serial engine is
> + * exclusively owned by this system processor. For shared controller
> + * configurations, another system processor may still be using the pins,
> + * and switching them to "sleep" can disrupt ongoing transfers.
> + */
> + if (!se->multi_owner) {
[Severity: High]
Where is se->multi_owner populated from the DeviceTree?
I don't see any parsing for the "qcom,qup-multi-owner" property in this
patch, which seems to leave the multi_owner flag permanently false. Does
this code require a device property read somewhere else to function?
[Severity: High]
Are runtime PM transitions handled correctly for shared controllers?
Looking at geni_se_resources_deactivate(), which is used by protocol drivers
like I2C for runtime suspend, it lacks a similar check:
drivers/soc/qcom/qcom-geni-se.c:geni_se_resources_deactivate() {
...
ret = pinctrl_pm_select_sleep_state(se->dev);
if (ret)
return ret;
...
}
If a shared controller goes into local runtime suspend, won't it still force
the pins into the sleep state and disrupt ongoing transfers on the other
processor?
> + ret = pinctrl_pm_select_sleep_state(se->dev);
> + if (ret)
> + return ret;
> + }
>
> geni_se_clks_off(se);
> return 0;
[ ... ]
> diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
> index 29a53bbc0dd49..725f89a445511 100644
> --- a/include/linux/soc/qcom/geni-se.h
> +++ b/include/linux/soc/qcom/geni-se.h
> @@ -78,6 +79,7 @@ struct geni_se {
> struct geni_icc_path icc_paths[3];
> struct dev_pm_domain_list *pd_list;
> bool has_opp;
> + bool multi_owner;
> };
[Severity: High]
Does this multi-owner configuration break transaction atomicity for
multi-message I2C reads?
Looking at the I2C driver, it disables multi-descriptor batching for reads:
drivers/i2c/busses/i2c-qcom-geni.c:geni_i2c_gpi_xfer() {
...
gi2c->is_tx_multi_desc_xfer = false;
break;
...
}
This causes DMA_PREP_INTERRUPT to be set on the write leg of the read
transaction.
The GPI DMA driver appears to interpret this flag as the end of the entire
batch and inserts an unlock command:
drivers/dma/qcom/gpi.c:gpi_create_i2c_tre() {
...
if (chan->multi_owner && is_last && i2c->op != I2C_READ) {
...
}
Will this prematurely release the hardware lock before the read leg executes,
allowing another processor to inject traffic and corrupt the bus?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260813140047.1006478-1-mukesh.savaliya@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode
2026-08-13 14:00 [PATCH v9 0/4] Enable multi-owner I2C support for QCOM GENI controllers Mukesh Kumar Savaliya
` (2 preceding siblings ...)
2026-08-13 14:00 ` [PATCH v9 3/4] soc: qcom: geni-se: Keep pinctrl active for multi-owner controllers Mukesh Kumar Savaliya
@ 2026-08-13 14:00 ` Mukesh Kumar Savaliya
2026-08-13 14:14 ` sashiko-bot
3 siblings, 1 reply; 9+ messages in thread
From: Mukesh Kumar Savaliya @ 2026-08-13 14:00 UTC (permalink / raw)
To: viken.dadhaniya, andi.shyti, robh, krzk+dt, conor+dt, vkoul,
Frank.Li, andersson, konradybcio, dmitry.baryshkov, linmq006,
zhengxingda, kees, agross, linux-arm-msm, linux-i2c, devicetree,
linux-kernel, dmaengine
Cc: krzysztof.kozlowski, bartosz.golaszewski, bjorn.andersson,
konrad.dybcio, brgl, Mukesh Kumar Savaliya
Some platforms use a QUP-based I2C controller in a configuration where the
controller is shared with another system processor. In this setup the
operating system must not assume exclusive ownership of the controller or
its associated pins.
Add support for enabling multi-owner operation when DeviceTree specifies
qcom,qup-multi-owner. When enabled, mark the underlying serial engine as
shared so the common GENI resource handling avoids selecting the "sleep"
pinctrl state, which could disrupt transfers initiated by the other
processor.
For GPI mode transfers, indicate multi-owner mode to the GPI driver by
setting the multi_owner field in the initial dmaengine_slave_config call.
The GPI driver then autonomously inserts lock and unlock TREs around the
transfer sequence without any per-message guidance from the I2C driver.
This keeps the locking mechanism as an internal GPI implementation
detail; the I2C driver does not need to track transfer boundaries or
message positions for lock/unlock purposes.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 658636c1ee0e..850fb0516a87 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -829,6 +829,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
peripheral.clk_div = itr->clk_div;
peripheral.set_config = 1;
peripheral.multi_msg = false;
+ peripheral.multi_owner = gi2c->se.multi_owner;
trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out,
itr->clk_div, itr->t_high_cnt,
@@ -1066,7 +1067,11 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
}
if (fifo_disable) {
- /* FIFO is disabled, so we can only use GPI DMA */
+ /*
+ * FIFO is disabled, so only GPI DMA can be used.
+ * In multi-owner configurations, the SE may be shared between subsystems,
+ * with each subsystem owning a separate GPII.
+ */
gi2c->gpi_mode = true;
ret = setup_gpi_dma(gi2c);
if (ret)
@@ -1075,6 +1080,11 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
dev_dbg(gi2c->se.dev, "Using GPI DMA mode for I2C\n");
} else {
gi2c->gpi_mode = false;
+
+ if (gi2c->se.multi_owner)
+ return dev_err_probe(gi2c->se.dev, -EINVAL,
+ "I2C sharing not supported in non-GSI mode\n");
+
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
/* I2C Master Hub Serial Elements doesn't have the HW_PARAM_0 register */
@@ -1145,6 +1155,11 @@ static int geni_i2c_probe(struct platform_device *pdev)
gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
}
+ if (device_property_present(&pdev->dev, "qcom,qup-multi-owner")) {
+ gi2c->se.multi_owner = true;
+ dev_dbg(&pdev->dev, "I2C controller is shared with another system processor\n");
+ }
+
if (has_acpi_companion(dev))
ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev));
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode
2026-08-13 14:00 ` [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode Mukesh Kumar Savaliya
@ 2026-08-13 14:14 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-08-13 14:14 UTC (permalink / raw)
To: Mukesh Kumar Savaliya
Cc: Frank.Li, dmaengine, conor+dt, devicetree, vkoul, robh
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Premature hardware lock release breaks multi-owner transaction atomicity
- [Medium] PM runtime usage counter leak on `geni_i2c_init` error path
Pre-existing issues:
- [Critical] Double free and use-after-free of DMA bounce buffers in `geni_i2c_gpi_xfer()`
- [High] Memory leak of DMA tracking arrays on multi-descriptor transfer error paths
--
--- Patch [4]: [PATCH v9 4/4] i2c: qcom-geni: Support multi-owner controllers in GPI mode ---
commit e932d5c4bcfb356d1e0cbaf5c247b0bd50b4e7e8
Author: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
i2c: qcom-geni: Support multi-owner controllers in GPI mode
Some platforms use a QUP-based I2C controller in a configuration where the
controller is shared with another system processor. In this setup the
operating system must not assume exclusive ownership of the controller or
its associated pins.
Add support for enabling multi-owner operation when DeviceTree specifies
qcom,qup-multi-owner.
> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
> --- a/drivers/i2c/busses/i2c-qcom-geni.c
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
> @@ -829,6 +829,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
> peripheral.clk_div = itr->clk_div;
> peripheral.set_config = 1;
> peripheral.multi_msg = false;
> + peripheral.multi_owner = gi2c->se.multi_owner;
[Severity: High]
Does this setup cause premature hardware lock releases, breaking the
multi-owner atomicity?
In geni_i2c_gpi(), the interrupt flag is unconditionally set for sequential
transfers:
geni_i2c_gpi() {
...
} else {
flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
}
...
}
Since the GPI driver inserts an UNLOCK TRE when DMA_PREP_INTERRUPT is set,
would a multi-message sequence like Write-then-Read drop the hardware lock
mid-transaction while the bus is stretched?
[Severity: Critical]
This is a pre-existing issue, but does geni_i2c_gpi_xfer() have a double free
bug on its DMA bounce buffers?
The tx_buf and rx_buf variables are initialized outside the loop, but are not
reset at the top of each iteration:
geni_i2c_gpi_xfer() {
void *tx_buf = NULL, *rx_buf = NULL;
...
for (i = 0; i < num; i++) {
...
if (msgs[i].flags & I2C_M_RD) {
geni_i2c_gpi(..., &rx_buf, ...);
}
...
geni_i2c_gpi_unmap(..., rx_buf, ...);
}
}
If a message sequence contains a Read followed by a Write, rx_buf is mapped
in the Read iteration and then freed in geni_i2c_gpi_unmap(). In the next
Write iteration, rx_buf is not reset to NULL and not overwritten, causing
geni_i2c_gpi_unmap() to use and free the stale pointer again:
geni_i2c_gpi_unmap() {
...
if (rx_buf) {
dma_unmap_single(...);
i2c_put_dma_safe_msg_buf(rx_buf, ...);
}
}
[Severity: High]
This is a pre-existing issue, but is there a memory leak of the DMA tracking
arrays on the multi-descriptor transfer error paths?
If a multi-descriptor transfer encounters an error (like a device NACK),
gi2c->err is set and the cleanup loop in geni_i2c_gpi_multi_desc_unmap()
aborts early:
geni_i2c_gpi_multi_desc_unmap() {
...
if (tx_multi_xfer->unmap_msg_cnt == gi2c->num_msgs - 1) {
kfree(tx_multi_xfer->dma_buf);
kfree(tx_multi_xfer->dma_addr);
break;
}
...
}
Since unmap_msg_cnt never reaches num_msgs - 1 on an early error path, are
dma_buf and dma_addr permanently leaked?
[ ... ]
> @@ -1075,6 +1080,11 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
> dev_dbg(gi2c->se.dev, "Using GPI DMA mode for I2C\n");
> } else {
> gi2c->gpi_mode = false;
> +
> + if (gi2c->se.multi_owner)
> + return dev_err_probe(gi2c->se.dev, -EINVAL,
> + "I2C sharing not supported in non-GSI mode\n");
> +
[Severity: Medium]
Does this early return leak the PM runtime usage counter?
By returning directly here, it seems to bypass the pm_runtime_put() cleanup
step at the err: label that the rest of the function uses on failure.
> tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260813140047.1006478-1-mukesh.savaliya@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 9+ messages in thread