* [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer
@ 2025-11-19 15:05 Benoît Monin
2025-11-19 15:05 ` [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers Benoît Monin
` (6 more replies)
0 siblings, 7 replies; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Add support for the I2C_M_STOP flag to the .xfer() function of the
designware driver. This allows grouping multiple accesses in a single
call and changing the target address after a STOP flag. This is achieved
by splitting i2c_dw_xfer() in two functions. The core logic handling
the transaction is now in __i2c_dw_xfer_one_part(), while i2c_dw_xfer()
loops over the messages to search for the I2C_M_STOP flag and calls
__i2c_dw_xfer_one_part().
Handle controllers that lack the ability to emit a RESTART when two
consecutive messages have the same address and direction by aborting
transfers that contain such a sequence of messages. For those controllers,
we also check that we do not get any unwanted STOP caused by a Tx FIFO
underrun, as they lack the ability to hold the clock during a transaction.
And we set the irq as non-threaded to prevent underrun on PREEMPT-RT
kernel when filling the FIFO.
The I2C controllers found in the EyeQ6Lplus and EyeQ7H SoCs from Mobileye
lack such capability, so a compatible string is added because this cannot
be detected at runtime.
This patch series also brings four cleanups:
* Optimize the read of the message flags in i2c_dw_read().
* Sort the compatible strings alphabetically in dw_i2c_of_match[].
* Simplify runtime PM handling in i2c_dw_xfer() with guard.
* Add a dedicated i2c_algorithm for AMD NAVI controller.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
Changes in v3:
- Add a compatible entry for Mobileye EyeQ7H i2c controllers.
- Use runtime PM macro for auto-cleanup in i2c_dw_xfer().
- Add a dedicated i2c_algorithm for AMD NAVI controllers.
- Rename __i2c_dw_xfer_unlocked() as it is not about locking at all.
- Rework the validity check of i2c_msg.
- Flag the irq as non-threaded for controllers that cannot hold the
clock to avoid underrun.
- Link to v2: https://lore.kernel.org/r/20251031-i2c-dw-v2-0-90416874fcc0@bootlin.com
Changes in v2:
- Add a single compatible for mobileye i2c controllers based in
DesignWare IP.
- Handle the I2C_M_STOP flag instead of emitting a STOP on target address
change.
- Abort transfer when it requires a RESTART on controller that cannot
emit them.
- Detect FIFO underrun instead of disabling threaded interrupt on
PREEMPT_RT kernel.
- Sort the compatible entries in a separate patch.
- Add a cleanup patch on flag reading in i2c_dw_read().
- Link to v1: https://lore.kernel.org/r/20251017-i2c-dw-v1-0-7b85b71c7a87@bootlin.com
---
Benoît Monin (7):
dt-bindings: i2c: dw: Add Mobileye I2C controllers
i2c: designware: Optimize flag reading in i2c_dw_read()
i2c: designware: Sort compatible strings in alphabetical order
i2c: designware: Use runtime PM macro for auto-cleanup
i2c: designware: Add dedicated algorithm for AMD NAVI
i2c: designware: Implement I2C_M_STOP support
i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled
.../bindings/i2c/snps,designware-i2c.yaml | 7 +
drivers/i2c/busses/i2c-designware-core.h | 1 +
drivers/i2c/busses/i2c-designware-master.c | 168 ++++++++++++++++-----
drivers/i2c/busses/i2c-designware-platdrv.c | 5 +-
4 files changed, 139 insertions(+), 42 deletions(-)
---
base-commit: 1ce5be103bd98b0d1a412c6685ebe49d4dddf2db
change-id: 20251014-i2c-dw-da315f758296
Best regards,
--
Benoît Monin, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-20 7:55 ` Krzysztof Kozlowski
2025-11-19 15:05 ` [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read() Benoît Monin
` (5 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Add compatible string for the DesignWare-based I2C controllers present
in Mobileye Eyeq6Lplus SoC, with a fallback to the default compatible.
The same controllers are also present in the EyeQ7H, so add a compatible
for those with a fallback to the Eyeq6Lplus compatible.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
index d904191bb0c6..914200188809 100644
--- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
@@ -34,8 +34,15 @@ properties:
- const: snps,designware-i2c
- description: Baikal-T1 SoC System I2C controller
const: baikal,bt1-sys-i2c
+ - description: Mobileye EyeQ DesignWare I2C controller
+ items:
+ - enum:
+ - mobileye,eyeq7h-i2c
+ - const: mobileye,eyeq6lplus-i2c
+ - const: snps,designware-i2c
- items:
- enum:
+ - mobileye,eyeq6lplus-i2c
- mscc,ocelot-i2c
- sophgo,sg2044-i2c
- thead,th1520-i2c
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read()
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
2025-11-19 15:05 ` [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 20:05 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 3/7] i2c: designware: Sort compatible strings in alphabetical order Benoît Monin
` (4 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Optimize the i2c_dw_read() function by reading the message flags only
once per message, rather than for every byte.
The message flags are accessed both in the outer loop and the inner loop,
so move the declaration of the local flags variable to the outer loop.
The message index is only modified by the outer loop, so reading the
flags in the inner loop was always getting the same value.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-master.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 41e9b5ecad20..ec4fc2708d03 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -586,11 +586,12 @@ i2c_dw_read(struct dw_i2c_dev *dev)
unsigned int rx_valid;
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
+ u32 flags = msgs[dev->msg_read_idx].flags;
unsigned int tmp;
u32 len;
u8 *buf;
- if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
+ if (!(flags & I2C_M_RD))
continue;
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
@@ -604,8 +605,6 @@ i2c_dw_read(struct dw_i2c_dev *dev)
regmap_read(dev->map, DW_IC_RXFLR, &rx_valid);
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
- u32 flags = msgs[dev->msg_read_idx].flags;
-
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
tmp &= DW_IC_DATA_CMD_DAT;
/* Ensure length byte is a valid value */
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 3/7] i2c: designware: Sort compatible strings in alphabetical order
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
2025-11-19 15:05 ` [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers Benoît Monin
2025-11-19 15:05 ` [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read() Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 15:05 ` [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup Benoît Monin
` (3 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Reorder the of_device_id structures so that they are in alphabetical
order. Also drop the unneeded inner trailing comma in the
"snps,designware-i2c" struct.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 34d881572351..d7d764f7554d 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -345,9 +345,9 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
}
static const struct of_device_id dw_i2c_of_match[] = {
- { .compatible = "snps,designware-i2c", },
- { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
+ { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
+ { .compatible = "snps,designware-i2c" },
{}
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
` (2 preceding siblings ...)
2025-11-19 15:05 ` [PATCH v3 3/7] i2c: designware: Sort compatible strings in alphabetical order Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 18:46 ` Andy Shevchenko
2025-11-20 10:34 ` Mika Westerberg
2025-11-19 15:05 ` [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI Benoît Monin
` (2 subsequent siblings)
6 siblings, 2 replies; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Simplify runtime PM handling in i2c_dw_xfer() by using the
pm_runtime_active_auto_try guard. This adds the proper handling for
runtime PM resume errors and allows us to get rid of the done_nolock
label.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-master.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index ec4fc2708d03..fe708c7cd282 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -811,12 +811,13 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
- pm_runtime_get_sync(dev->dev);
+ ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
+ if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
+ return -ENXIO;
switch (dev->flags & MODEL_MASK) {
case MODEL_AMD_NAVI_GPU:
- ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
- goto done_nolock;
+ return amd_i2c_dw_xfer_quirk(adap, msgs, num);
default:
break;
}
@@ -834,7 +835,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = i2c_dw_acquire_lock(dev);
if (ret)
- goto done_nolock;
+ return ret;
ret = i2c_dw_wait_bus_not_busy(dev);
if (ret < 0)
@@ -899,9 +900,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
done:
i2c_dw_release_lock(dev);
-done_nolock:
- pm_runtime_put_autosuspend(dev->dev);
-
return ret;
}
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
` (3 preceding siblings ...)
2025-11-19 15:05 ` [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 20:04 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support Benoît Monin
2025-11-19 15:05 ` [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled Benoît Monin
6 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Apart from runtime PM, there is nothing in common between i2c_dw_xfer()
and amd_i2c_dw_xfer_quirk(), so give AMD NAVI controller its own algorithm
instead of calling the quirk from i2c_dw_xfer().
Add runtime PM handling to amd_i2c_dw_xfer_quirk() and a dedicated
i2c_algorithm for AMD NAVI controllers. The adapter algorithm is set
during probe based on the device model.
This way we avoid checking for the device model at the start of every
transfer.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-master.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index fe708c7cd282..12a1194bec52 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -354,6 +354,10 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
u8 *tx_buf;
unsigned int val;
+ ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
+ if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
+ return -ENXIO;
+
/*
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
* it is mandatory to set the right value in specific register
@@ -815,13 +819,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
return -ENXIO;
- switch (dev->flags & MODEL_MASK) {
- case MODEL_AMD_NAVI_GPU:
- return amd_i2c_dw_xfer_quirk(adap, msgs, num);
- default:
- break;
- }
-
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -908,6 +905,11 @@ static const struct i2c_algorithm i2c_dw_algo = {
.functionality = i2c_dw_func,
};
+static const struct i2c_algorithm amd_i2c_dw_algo = {
+ .xfer = amd_i2c_dw_xfer_quirk,
+ .functionality = i2c_dw_func,
+};
+
static const struct i2c_adapter_quirks i2c_dw_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
};
@@ -1043,7 +1045,10 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
scnprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C adapter");
adap->retries = 3;
- adap->algo = &i2c_dw_algo;
+ if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+ adap->algo = &amd_i2c_dw_algo;
+ else
+ adap->algo = &i2c_dw_algo;
adap->quirks = &i2c_dw_quirks;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
` (4 preceding siblings ...)
2025-11-19 15:05 ` [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 20:03 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled Benoît Monin
6 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
Add the support of the I2C_M_STOP flag in i2c_msg by splitting
i2c_dw_xfer() in two: __i2c_dw_xfer_one_part() for the core transfer logic
and i2c_dw_xfer() for handling the high-level transaction management.
In detail __i2c_dw_xfer_one_part() starts a transaction and wait for its
completion, either with a STOP on the bus or an error. i2c_dw_xfer()
loops over the messages to search for the I2C_M_STOP flag and calls
__i2c_dw_xfer_one_part() for each part of the messages up to a STOP or
the end of the messages array.
i2c_dw_xfer() takes care of runtime PM and holds the hardware lock on
the bus while calling __i2c_dw_xfer_one_part(), this allows grouping
multiple accesses to device that support a STOP in a transaction when
done via i2c_dev I2C_RDWR ioctl.
Also, now that we have a lookup of the messages in i2c_dw_xfer() prior
to each transaction, we use it to make sure the messages are valid for
the transaction. We check that the target address does not change before
starting the transaction instead of aborting the transfer while it is
happening, as it was done in i2c_dw_xfer_msg(). The target address can
only be changed after an I2C_M_STOP flag, i.e after a STOP on the i2c bus.
The I2C_FUNC_PROTOCOL_MANGLING flag is added to the list of
functionalities supported by the controller, except for the AMD NAVI
i2c controller which uses its own xfer() function and is left untouched.
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-master.c | 108 +++++++++++++++++++++--------
1 file changed, 81 insertions(+), 27 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 12a1194bec52..2977c13c709c 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -435,7 +435,6 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
struct i2c_msg *msgs = dev->msgs;
u32 intr_mask;
int tx_limit, rx_limit;
- u32 addr = msgs[dev->msg_write_idx].addr;
u32 buf_len = dev->tx_buf_len;
u8 *buf = dev->tx_buf;
bool need_restart = false;
@@ -446,18 +445,6 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
u32 flags = msgs[dev->msg_write_idx].flags;
- /*
- * If target address has changed, we need to
- * reprogram the target address in the I2C
- * adapter when we are done with this transfer.
- */
- if (msgs[dev->msg_write_idx].addr != addr) {
- dev_err(dev->dev,
- "%s: invalid target address\n", __func__);
- dev->msg_err = -EINVAL;
- break;
- }
-
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
/* new i2c_msg */
buf = msgs[dev->msg_write_idx].buf;
@@ -805,20 +792,15 @@ static int i2c_dw_wait_transfer(struct dw_i2c_dev *dev)
}
/*
- * Prepare controller for a transaction and call i2c_dw_xfer_msg.
+ * Prepare controller for a transaction, start the transfer of the msgs
+ * and wait for completion, either a STOP or a error.
+ * Return the number of messages transferred or a negative error code.
*/
static int
-i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+__i2c_dw_xfer_one_part(struct dw_i2c_dev *dev, struct i2c_msg *msgs, size_t num)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
int ret;
- dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
-
- ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
- if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
- return -ENXIO;
-
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -830,10 +812,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev->abort_source = 0;
dev->rx_outstanding = 0;
- ret = i2c_dw_acquire_lock(dev);
- if (ret)
- return ret;
-
ret = i2c_dw_wait_bus_not_busy(dev);
if (ret < 0)
goto done;
@@ -894,10 +872,83 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = -EIO;
+done:
+ return ret;
+}
+
+/*
+ * Check that the message pointed by @idx in the @msgs array
+ * respects the limitation of the controller.
+ */
+static inline bool
+i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t idx)
+{
+ /* The first message of a transaction is valid */
+ if (!idx)
+ return true;
+
+ /*
+ * We cannot change the target address during a transaction, so make
+ * sure the address is identical to the one of the previous message.
+ */
+ if (msgs[idx - 1].addr != msgs[idx].addr) {
+ dev_err(dev->dev, "invalid target address\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int
+i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ struct i2c_msg *msgs_part;
+ size_t cnt;
+ int ret;
+
+ dev_dbg(dev->dev, "msgs: %d\n", num);
+
+ ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
+ if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
+ return -ENXIO;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * If the I2C_M_STOP is present in some the messages,
+ * we do one transaction for each part up to the STOP.
+ */
+ for (msgs_part = msgs; msgs_part < msgs + num; msgs_part += cnt) {
+ /*
+ * Count the messages in a transaction, up to a STOP
+ * or the end of the msgs.
+ */
+ for (cnt = 1; ; cnt++) {
+ if (!i2c_dw_msg_is_valid(dev, msgs_part, cnt - 1)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((msgs_part[cnt - 1].flags & I2C_M_STOP) ||
+ (msgs_part + cnt == msgs + num))
+ break;
+ }
+
+ /* transfer one part up to a STOP */
+ ret = __i2c_dw_xfer_one_part(dev, msgs_part, cnt);
+ if (ret < 0)
+ break;
+ }
+
done:
i2c_dw_release_lock(dev);
- return ret;
+ if (ret < 0)
+ return ret;
+ return num;
}
static const struct i2c_algorithm i2c_dw_algo = {
@@ -920,6 +971,9 @@ void i2c_dw_configure_master(struct dw_i2c_dev *dev)
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
+ if ((dev->flags & MODEL_MASK) != MODEL_AMD_NAVI_GPU)
+ dev->functionality |= I2C_FUNC_PROTOCOL_MANGLING;
+
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN;
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
` (5 preceding siblings ...)
2025-11-19 15:05 ` [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support Benoît Monin
@ 2025-11-19 15:05 ` Benoît Monin
2025-11-19 20:15 ` Andy Shevchenko
6 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-19 15:05 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel, Benoît Monin
If IC_EMPTYFIFO_HOLD_MASTER_EN parameter is 0, "Stop" and "Repeated Start"
bits in command register does not exist, thus it is impossible to send
several consecutive write messages in a single hardware batch. The
existing implementation worked with such configuration incorrectly:
all consecutive write messages are joined into a single message without
any Start/Stop or Repeated Start conditions. For example, the following
command:
i2ctransfer -y 0 w1@0x55 0x00 w1@0x55 0x01
does the same as
i2ctransfer -y 0 w2@0x55 0x00 0x01
In i2c_dw_msg_is_valid(), we ensure that we do not have such sequence
of messages requiring a RESTART, aborting the transfer on controller
that cannot emit them explicitly.
This behavior is activated by compatible entries because the state of
the IC_EMPTYFIFO_HOLD_MASTER_EN parameter cannot be detected at runtime.
Add the compatible entry for Mobileye SoCs needing the workaround.
There is another possible problem with this controller configuration:
When the CPU is putting commands to the FIFO, this process must not be
interrupted because if FIFO buffer gets empty, the controller finishes
the I2C transaction and generates STOP condition on the bus.
If we continue writing the remainder of the message to the FIFO, the
controller will start emitting a new transaction with those data. This
turns a single a single message into multiple I2C transactions. To
protect against FIFO underrun, two changes are done:
First we flag the interrupt with IRQF_NO_THREAD, to prevent it from
running in a thread on PREEMPT-RT kernel. This ensures that we are not
interrupted when filling the FIFO as it is very time-senstive. For
example, being preempted after writing a single byte in the FIFO with
a 1MHz bus gives us only 18µs before an underrun.
Second in i2c_dw_process_transfer(), we abort if a STOP is detected
while a read or a write is in progress. This can occur when processing
a message larger than the FIFO. In that case the message is processed in
parts, and rely on the TX EMPTY interrupt to refill the FIFO when it gets
below a threshold. If servicing this interrupt is delayed for too long,
it can trigger a FIFO underrun, thus an unwanted STOP.
Originally-by: Dmitry Guzman <dmitry.guzman@mobileye.com>
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
drivers/i2c/busses/i2c-designware-core.h | 1 +
drivers/i2c/busses/i2c-designware-master.c | 32 +++++++++++++++++++++++++++++
drivers/i2c/busses/i2c-designware-platdrv.c | 1 +
3 files changed, 34 insertions(+)
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 347843b4f5dd..a31a8698e511 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -311,6 +311,7 @@ struct dw_i2c_dev {
#define ACCESS_NO_IRQ_SUSPEND BIT(1)
#define ARBITRATION_SEMAPHORE BIT(2)
#define ACCESS_POLLING BIT(3)
+#define NO_EMPTYFIFO_HOLD_MASTER BIT(4)
#define MODEL_MSCC_OCELOT BIT(8)
#define MODEL_BAIKAL_BT1 BIT(9)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 2977c13c709c..3486a8c760e0 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -710,6 +710,14 @@ static void i2c_dw_process_transfer(struct dw_i2c_dev *dev, unsigned int stat)
if (stat & DW_IC_INTR_TX_EMPTY)
i2c_dw_xfer_msg(dev);
+ /* Abort if we detect a STOP in the middle of a read or a write */
+ if ((stat & DW_IC_INTR_STOP_DET) &&
+ (dev->status & (STATUS_READ_IN_PROGRESS | STATUS_WRITE_IN_PROGRESS))) {
+ dev_err(dev->dev, "spurious STOP detected\n");
+ dev->rx_outstanding = 0;
+ dev->msg_err = -EIO;
+ }
+
/*
* No need to modify or disable the interrupt mask here.
* i2c_dw_xfer_msg() will take care of it according to
@@ -896,6 +904,16 @@ i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t i
return false;
}
+ /*
+ * Make sure we don't need explicit RESTART between two messages
+ * in the same direction for controllers that cannot emit them.
+ */
+ if (dev->flags & NO_EMPTYFIFO_HOLD_MASTER &&
+ (msgs[idx - 1].flags & I2C_M_RD) == (msgs[idx].flags & I2C_M_RD)) {
+ dev_err(dev->dev, "cannot emit RESTART\n");
+ return false;
+ }
+
return true;
}
@@ -1113,6 +1131,20 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
}
+ /*
+ * The first writing to TX FIFO buffer causes transmission start. If
+ * IC_EMPTYFIFO_HOLD_MASTER_EN is not set, when TX FIFO gets empty, I2C
+ * controller finishes the transaction. If writing to FIFO is
+ * interrupted, FIFO can get empty and the transaction will be finished
+ * prematurely. FIFO buffer is filled in IRQ handler, but in PREEMPT_RT
+ * kernel IRQ handler by default is executed in thread that can be
+ * preempted with another higher priority thread or an interrupt. So,
+ * IRQF_NO_THREAD flag is required in order to prevent any preemption
+ * when filling the FIFO.
+ */
+ if (dev->flags & NO_EMPTYFIFO_HOLD_MASTER)
+ irq_flags |= IRQF_NO_THREAD;
+
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index d7d764f7554d..4aad3dc51fbc 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -346,6 +346,7 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
+ { .compatible = "mobileye,eyeq6lplus-i2c", .data = (void *)NO_EMPTYFIFO_HOLD_MASTER },
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
{ .compatible = "snps,designware-i2c" },
{}
--
2.51.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup
2025-11-19 15:05 ` [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup Benoît Monin
@ 2025-11-19 18:46 ` Andy Shevchenko
2025-11-20 10:34 ` Mika Westerberg
1 sibling, 0 replies; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-19 18:46 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:33PM +0100, Benoît Monin wrote:
> Simplify runtime PM handling in i2c_dw_xfer() by using the
> pm_runtime_active_auto_try guard. This adds the proper handling for
> runtime PM resume errors and allows us to get rid of the done_nolock
> label.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
LGTM and order kept the same, shouldn't regress.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support
2025-11-19 15:05 ` [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support Benoît Monin
@ 2025-11-19 20:03 ` Andy Shevchenko
0 siblings, 0 replies; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-19 20:03 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:35PM +0100, Benoît Monin wrote:
> Add the support of the I2C_M_STOP flag in i2c_msg by splitting
> i2c_dw_xfer() in two: __i2c_dw_xfer_one_part() for the core transfer logic
> and i2c_dw_xfer() for handling the high-level transaction management.
>
> In detail __i2c_dw_xfer_one_part() starts a transaction and wait for its
> completion, either with a STOP on the bus or an error. i2c_dw_xfer()
> loops over the messages to search for the I2C_M_STOP flag and calls
> __i2c_dw_xfer_one_part() for each part of the messages up to a STOP or
> the end of the messages array.
>
> i2c_dw_xfer() takes care of runtime PM and holds the hardware lock on
> the bus while calling __i2c_dw_xfer_one_part(), this allows grouping
> multiple accesses to device that support a STOP in a transaction when
> done via i2c_dev I2C_RDWR ioctl.
>
> Also, now that we have a lookup of the messages in i2c_dw_xfer() prior
> to each transaction, we use it to make sure the messages are valid for
> the transaction. We check that the target address does not change before
> starting the transaction instead of aborting the transfer while it is
> happening, as it was done in i2c_dw_xfer_msg(). The target address can
> only be changed after an I2C_M_STOP flag, i.e after a STOP on the i2c bus.
>
> The I2C_FUNC_PROTOCOL_MANGLING flag is added to the list of
> functionalities supported by the controller, except for the AMD NAVI
> i2c controller which uses its own xfer() function and is left untouched.
...
> ret = -EIO;
>
> +done:
> + return ret;
I don't see the point of keeping this label anymore. Is there a followup
that addresses this?
> +static inline bool
> +i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t idx)
> +{
> + /* The first message of a transaction is valid */
> + if (!idx)
> + return true;
> +
> + /*
> + * We cannot change the target address during a transaction, so make
> + * sure the address is identical to the one of the previous message.
> + */
> + if (msgs[idx - 1].addr != msgs[idx].addr) {
> + dev_err(dev->dev, "invalid target address\n");
> + return false;
> + }
> +
> + return true;
> +}
Hmm... I would do it in the nested conditionals if nothing comes between
the lines in the future.
static inline bool
i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t idx)
{
/*
* We cannot change the target address during a transaction, so make
* sure the address of the sequential messages is identical to the one
* of the previous message.
*/
if (idx && (msgs[idx - 1].addr != msgs[idx].addr) {
dev_err(dev->dev, "invalid target address\n");
return false;
}
/*
* Addresses match. For the first message return true as it sets the
* address for the whole transaction.
*/
return true;
}
...
> +static int
> +i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
> +{
> + struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
> + struct i2c_msg *msgs_part;
> + size_t cnt;
> + int ret;
> +
> + dev_dbg(dev->dev, "msgs: %d\n", num);
> +
> + ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
> + if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
> + return -ENXIO;
> +
> + ret = i2c_dw_acquire_lock(dev);
> + if (ret)
> + return ret;
> +
> + /*
> + * If the I2C_M_STOP is present in some the messages,
> + * we do one transaction for each part up to the STOP.
> + */
> + for (msgs_part = msgs; msgs_part < msgs + num; msgs_part += cnt) {
> + /*
> + * Count the messages in a transaction, up to a STOP
> + * or the end of the msgs.
> + */
> + for (cnt = 1; ; cnt++) {
> + if (!i2c_dw_msg_is_valid(dev, msgs_part, cnt - 1)) {
> + ret = -EINVAL;
> + goto done;
What about
break;
> + }
> +
> + if ((msgs_part[cnt - 1].flags & I2C_M_STOP) ||
> + (msgs_part + cnt == msgs + num))
> + break;
> + }
if (ret < 0)
break;
?
The motivation is to avoid mixing auto ptr with goto style
(it's not recommended as per cleanup.h).
> + /* transfer one part up to a STOP */
> + ret = __i2c_dw_xfer_one_part(dev, msgs_part, cnt);
> + if (ret < 0)
> + break;
> + }
> +
> done:
> i2c_dw_release_lock(dev);
>
> - return ret;
> + if (ret < 0)
> + return ret;
> + return num;
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI
2025-11-19 15:05 ` [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI Benoît Monin
@ 2025-11-19 20:04 ` Andy Shevchenko
0 siblings, 0 replies; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-19 20:04 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:34PM +0100, Benoît Monin wrote:
> Apart from runtime PM, there is nothing in common between i2c_dw_xfer()
> and amd_i2c_dw_xfer_quirk(), so give AMD NAVI controller its own algorithm
> instead of calling the quirk from i2c_dw_xfer().
>
> Add runtime PM handling to amd_i2c_dw_xfer_quirk() and a dedicated
> i2c_algorithm for AMD NAVI controllers. The adapter algorithm is set
> during probe based on the device model.
>
> This way we avoid checking for the device model at the start of every
> transfer.
Makes sense
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read()
2025-11-19 15:05 ` [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read() Benoît Monin
@ 2025-11-19 20:05 ` Andy Shevchenko
0 siblings, 0 replies; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-19 20:05 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:31PM +0100, Benoît Monin wrote:
> Optimize the i2c_dw_read() function by reading the message flags only
> once per message, rather than for every byte.
>
> The message flags are accessed both in the outer loop and the inner loop,
> so move the declaration of the local flags variable to the outer loop.
>
> The message index is only modified by the outer loop, so reading the
> flags in the inner loop was always getting the same value.
OK.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled
2025-11-19 15:05 ` [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled Benoît Monin
@ 2025-11-19 20:15 ` Andy Shevchenko
2025-11-25 10:45 ` Benoît Monin
0 siblings, 1 reply; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-19 20:15 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:36PM +0100, Benoît Monin wrote:
> If IC_EMPTYFIFO_HOLD_MASTER_EN parameter is 0, "Stop" and "Repeated Start"
> bits in command register does not exist, thus it is impossible to send
> several consecutive write messages in a single hardware batch. The
> existing implementation worked with such configuration incorrectly:
> all consecutive write messages are joined into a single message without
> any Start/Stop or Repeated Start conditions. For example, the following
> command:
>
> i2ctransfer -y 0 w1@0x55 0x00 w1@0x55 0x01
>
> does the same as
>
> i2ctransfer -y 0 w2@0x55 0x00 0x01
>
> In i2c_dw_msg_is_valid(), we ensure that we do not have such sequence
> of messages requiring a RESTART, aborting the transfer on controller
> that cannot emit them explicitly.
>
> This behavior is activated by compatible entries because the state of
> the IC_EMPTYFIFO_HOLD_MASTER_EN parameter cannot be detected at runtime.
> Add the compatible entry for Mobileye SoCs needing the workaround.
>
> There is another possible problem with this controller configuration:
> When the CPU is putting commands to the FIFO, this process must not be
> interrupted because if FIFO buffer gets empty, the controller finishes
> the I2C transaction and generates STOP condition on the bus.
>
> If we continue writing the remainder of the message to the FIFO, the
> controller will start emitting a new transaction with those data. This
> turns a single a single message into multiple I2C transactions. To
> protect against FIFO underrun, two changes are done:
>
> First we flag the interrupt with IRQF_NO_THREAD, to prevent it from
> running in a thread on PREEMPT-RT kernel. This ensures that we are not
> interrupted when filling the FIFO as it is very time-senstive. For
> example, being preempted after writing a single byte in the FIFO with
> a 1MHz bus gives us only 18µs before an underrun.
>
> Second in i2c_dw_process_transfer(), we abort if a STOP is detected
> while a read or a write is in progress. This can occur when processing
> a message larger than the FIFO. In that case the message is processed in
> parts, and rely on the TX EMPTY interrupt to refill the FIFO when it gets
> below a threshold. If servicing this interrupt is delayed for too long,
> it can trigger a FIFO underrun, thus an unwanted STOP.
...
> #define ACCESS_NO_IRQ_SUSPEND BIT(1)
> #define ARBITRATION_SEMAPHORE BIT(2)
> #define ACCESS_POLLING BIT(3)
> +#define NO_EMPTYFIFO_HOLD_MASTER BIT(4)
Can we name it
#define ACCESS_NO_EMPTYFIFO_HOLD_MASTER BIT(4)
?
It will at least keep them under same namespace.
...
> i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t i
> + /*
> + * Make sure we don't need explicit RESTART between two messages
> + * in the same direction for controllers that cannot emit them.
> + */
> + if (dev->flags & NO_EMPTYFIFO_HOLD_MASTER &&
> + (msgs[idx - 1].flags & I2C_M_RD) == (msgs[idx].flags & I2C_M_RD)) {
> + dev_err(dev->dev, "cannot emit RESTART\n");
> + return false;
> + }
Ah, Now I see the point of checking the idx first, but can we rather call it
with idx >= 1 to begin with?
> return true;
> }
...
> + /*
> + * The first writing to TX FIFO buffer causes transmission start. If
> + * IC_EMPTYFIFO_HOLD_MASTER_EN is not set, when TX FIFO gets empty, I2C
> + * controller finishes the transaction. If writing to FIFO is
Make the lines more equal by length.
* The first writing to TX FIFO buffer causes transmission start.
* If IC_EMPTYFIFO_HOLD_MASTER_EN is not set, when TX FIFO gets empty,
* I2C controller finishes the transaction. If writing to FIFO is
> + * interrupted, FIFO can get empty and the transaction will be finished
> + * prematurely. FIFO buffer is filled in IRQ handler, but in PREEMPT_RT
> + * kernel IRQ handler by default is executed in thread that can be
> + * preempted with another higher priority thread or an interrupt. So,
> + * IRQF_NO_THREAD flag is required in order to prevent any preemption
> + * when filling the FIFO.
Similarly
* preempted with another higher priority thread or an interrupt.
* So, IRQF_NO_THREAD flag is required in order to prevent any
* preemption when filling the FIFO.
Dunno if the middle part needs to be rewrapped, perhaps so...
> + */
...
> { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
> + { .compatible = "mobileye,eyeq6lplus-i2c", .data = (void *)NO_EMPTYFIFO_HOLD_MASTER },
Are you expecting more with this? I would rather use a compatible matching
instead of the flag,
> { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
> { .compatible = "snps,designware-i2c" },
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers
2025-11-19 15:05 ` [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers Benoît Monin
@ 2025-11-20 7:55 ` Krzysztof Kozlowski
0 siblings, 0 replies; 17+ messages in thread
From: Krzysztof Kozlowski @ 2025-11-20 7:55 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:30PM +0100, Benoît Monin wrote:
> Add compatible string for the DesignWare-based I2C controllers present
> in Mobileye Eyeq6Lplus SoC, with a fallback to the default compatible.
> The same controllers are also present in the EyeQ7H, so add a compatible
> for those with a fallback to the Eyeq6Lplus compatible.
>
> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
> ---
> Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup
2025-11-19 15:05 ` [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup Benoît Monin
2025-11-19 18:46 ` Andy Shevchenko
@ 2025-11-20 10:34 ` Mika Westerberg
1 sibling, 0 replies; 17+ messages in thread
From: Mika Westerberg @ 2025-11-20 10:34 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Andy Shevchenko, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Wed, Nov 19, 2025 at 04:05:33PM +0100, Benoît Monin wrote:
> Simplify runtime PM handling in i2c_dw_xfer() by using the
> pm_runtime_active_auto_try guard. This adds the proper handling for
> runtime PM resume errors and allows us to get rid of the done_nolock
> label.
>
> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
> ---
> drivers/i2c/busses/i2c-designware-master.c | 12 +++++-------
> 1 file changed, 5 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
> index ec4fc2708d03..fe708c7cd282 100644
> --- a/drivers/i2c/busses/i2c-designware-master.c
> +++ b/drivers/i2c/busses/i2c-designware-master.c
> @@ -811,12 +811,13 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>
> dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
>
> - pm_runtime_get_sync(dev->dev);
> + ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
> + if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
> + return -ENXIO;
>
> switch (dev->flags & MODEL_MASK) {
> case MODEL_AMD_NAVI_GPU:
> - ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
> - goto done_nolock;
> + return amd_i2c_dw_xfer_quirk(adap, msgs, num);
> default:
> break;
> }
> @@ -834,7 +835,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>
> ret = i2c_dw_acquire_lock(dev);
> if (ret)
> - goto done_nolock;
> + return ret;
>
> ret = i2c_dw_wait_bus_not_busy(dev);
> if (ret < 0)
> @@ -899,9 +900,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> done:
> i2c_dw_release_lock(dev);
There is still this goto here. I don't think it is good to mix the
"cleanup" helpers and gotos. So if we want to use the cleanup helpers then
I think we should convert the whole function.
>
> -done_nolock:
> - pm_runtime_put_autosuspend(dev->dev);
> -
> return ret;
> }
>
>
> --
> 2.51.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled
2025-11-19 20:15 ` Andy Shevchenko
@ 2025-11-25 10:45 ` Benoît Monin
2025-11-25 11:12 ` Andy Shevchenko
0 siblings, 1 reply; 17+ messages in thread
From: Benoît Monin @ 2025-11-25 10:45 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
Hi Andy,
Thanks for the reviews.
(ack on other comments)
On Wednesday, 19 November 2025 at 21:15:16 CET, Andy Shevchenko wrote:
> On Wed, Nov 19, 2025 at 04:05:36PM +0100, Benoît Monin wrote:
[...]
> > i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t i
>
> > + /*
> > + * Make sure we don't need explicit RESTART between two messages
> > + * in the same direction for controllers that cannot emit them.
> > + */
> > + if (dev->flags & NO_EMPTYFIFO_HOLD_MASTER &&
> > + (msgs[idx - 1].flags & I2C_M_RD) == (msgs[idx].flags & I2C_M_RD)) {
> > + dev_err(dev->dev, "cannot emit RESTART\n");
> > + return false;
> > + }
>
> Ah, Now I see the point of checking the idx first, but can we rather call it
> with idx >= 1 to begin with?
>
We would still have to check it when calling i2c_dw_msg_is_valid(), as the
first message after a STOP don't have any limitation. It is not just for
protecting against an out-of-bound access to msgs. The validity of a
message is in relation to the previous message in the same transaction.
I will change the comment to make this clearer.
> > return true;
> > }
[...]
> > { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
> > + { .compatible = "mobileye,eyeq6lplus-i2c", .data = (void *)NO_EMPTYFIFO_HOLD_MASTER },
>
> Are you expecting more with this? I would rather use a compatible matching
> instead of the flag,
>
The IC_EMPTYFIFO_HOLD_MASTER_EN parameter is part of the DesignWare IP, it
is not specific to Mobileye. Given that typical i2c accesses (single read,
single write, write-then-read) work on non-PREMPT_RT without this patch, I
suspect there are other controllers that lack the ability to hold the clock
when the FIFO is empty that could benefit from this flag.
> > { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
> > { .compatible = "snps,designware-i2c" },
>
>
Best regards,
--
Benoît Monin, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled
2025-11-25 10:45 ` Benoît Monin
@ 2025-11-25 11:12 ` Andy Shevchenko
0 siblings, 0 replies; 17+ messages in thread
From: Andy Shevchenko @ 2025-11-25 11:12 UTC (permalink / raw)
To: Benoît Monin
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jarkko Nikula, Mika Westerberg, Jan Dabros,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Thomas Petazzoni, Gregory CLEMENT, Théo Lebrun,
Tawfik Bayouk, Vladimir Kondratiev, Dmitry Guzman, linux-i2c,
devicetree, linux-kernel, linux-rt-devel
On Tue, Nov 25, 2025 at 11:45:35AM +0100, Benoît Monin wrote:
> On Wednesday, 19 November 2025 at 21:15:16 CET, Andy Shevchenko wrote:
> > On Wed, Nov 19, 2025 at 04:05:36PM +0100, Benoît Monin wrote:
[...]
> > > + /*
> > > + * Make sure we don't need explicit RESTART between two messages
> > > + * in the same direction for controllers that cannot emit them.
> > > + */
> > > + if (dev->flags & NO_EMPTYFIFO_HOLD_MASTER &&
> > > + (msgs[idx - 1].flags & I2C_M_RD) == (msgs[idx].flags & I2C_M_RD)) {
> > > + dev_err(dev->dev, "cannot emit RESTART\n");
> > > + return false;
> > > + }
> >
> > Ah, Now I see the point of checking the idx first, but can we rather call it
> > with idx >= 1 to begin with?
> >
> We would still have to check it when calling i2c_dw_msg_is_valid(), as the
> first message after a STOP don't have any limitation. It is not just for
> protecting against an out-of-bound access to msgs. The validity of a
> message is in relation to the previous message in the same transaction.
>
> I will change the comment to make this clearer.
OK!
> > > return true;
[...]
> > > { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
> > > + { .compatible = "mobileye,eyeq6lplus-i2c", .data = (void *)NO_EMPTYFIFO_HOLD_MASTER },
> >
> > Are you expecting more with this? I would rather use a compatible matching
> > instead of the flag,
> >
> The IC_EMPTYFIFO_HOLD_MASTER_EN parameter is part of the DesignWare IP, it
> is not specific to Mobileye. Given that typical i2c accesses (single read,
> single write, write-then-read) work on non-PREMPT_RT without this patch, I
> suspect there are other controllers that lack the ability to hold the clock
> when the FIFO is empty that could benefit from this flag.
USB DWC3 driver uses properties for that. We really should go away from this
MODEL_*/FLAG_* legacy approach in the driver. Either compatible string or
a specific property would be better than inventing yet another no so scalable
flag.
> > > { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
> > > { .compatible = "snps,designware-i2c" },
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2025-11-25 11:13 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-19 15:05 [PATCH v3 0/7] i2c: designware: Improve support of multi-messages transfer Benoît Monin
2025-11-19 15:05 ` [PATCH v3 1/7] dt-bindings: i2c: dw: Add Mobileye I2C controllers Benoît Monin
2025-11-20 7:55 ` Krzysztof Kozlowski
2025-11-19 15:05 ` [PATCH v3 2/7] i2c: designware: Optimize flag reading in i2c_dw_read() Benoît Monin
2025-11-19 20:05 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 3/7] i2c: designware: Sort compatible strings in alphabetical order Benoît Monin
2025-11-19 15:05 ` [PATCH v3 4/7] i2c: designware: Use runtime PM macro for auto-cleanup Benoît Monin
2025-11-19 18:46 ` Andy Shevchenko
2025-11-20 10:34 ` Mika Westerberg
2025-11-19 15:05 ` [PATCH v3 5/7] i2c: designware: Add dedicated algorithm for AMD NAVI Benoît Monin
2025-11-19 20:04 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 6/7] i2c: designware: Implement I2C_M_STOP support Benoît Monin
2025-11-19 20:03 ` Andy Shevchenko
2025-11-19 15:05 ` [PATCH v3 7/7] i2c: designware: Support of controller with IC_EMPTYFIFO_HOLD_MASTER disabled Benoît Monin
2025-11-19 20:15 ` Andy Shevchenko
2025-11-25 10:45 ` Benoît Monin
2025-11-25 11:12 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).