* [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support
@ 2026-09-25 6:56 Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 1/3] dt-bindings: i2c: dw: Add ti,tda54-i2c compatible Aniket Limaye
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Aniket Limaye @ 2026-09-25 6:56 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Andy Shevchenko,
Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter
Cc: linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel,
Aniket Limaye
Add new compatible and update driver to support Synopsys Advanced I2C
Controller (DWC_i2c [0]) present on TI TDA54 SoC. This is needed since
this controller differs from the existing designware i2c (DW_apb_i2c
[1]) in its register offsets and some register definitions.
The new compatible updates driver logic due to changes in register
definitions:
- The new register offsets are handled by first refactoring the driver
to use a map of register IDs to their offsets.
- Similarly, also update the driver to use an updated CON-register
bitfield definition.
- Interrupts are acknowledged by writing a bitmask to a single CLR_INTR
register instead of reading N dedicated CLR_* registers;
i2c_dw_ack_intr() picks the right method based on dev->flags.
- One HCNT/LCNT register pair is shared between standard and fast speed
instead of having one pair each; i2c_dw_write_timings() writes
whichever value set matches the configured speed.
- No COMP_PARAM_1 register, so FIFO depth and high-speed-mode support
can't be autodetected: FIFO depth now comes from the required
snps,tx-fifo-depth/snps,rx-fifo-depth DT properties, and the
high-speed capability check is skipped.
- No defined CON.RESTART_EN bit; treat it as always set.
- TDA54 SoC does not support SMBus, so skip accesses to those registers.
[0]: DWC_i2c_reference.pdf
[1]: DW_apb_i2c_databook.pdf
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
Changes in v3:
- [PATCH 2/3] Revert to special handling for the max_register in regmap
for MODEL_AMD_NAVI_GPU instead of unconditionally including
AMD_UCSI_INTR_REG in the legal regmap range. (sashiko)
- Link to v2: https://lore.kernel.org/r/20260925-tda54-upstream-i2c-v2-0-261785e58493@ti.com
Changes in v2:
- Rename compatible/flag: snps,dwc-i2c -> ti,tda54-i2c,
MODEL_DWC_I2C -> MODEL_TI_TDA54.
- Move the Reg ID -> Offset mapping inside the regmap functions such
that existing calls to reg_read/write are unaltered. This does change
the reg_read()/reg_write() functions to take reg IDs as input rather
than their physical offsets, which also requires updates to the
xe_i2c.c driver to handle the same mapping.
- Remove i2c_dwc_has_smbus(); just skip the SMBUS_INTR_MASK write under
MODEL_TI_TDA54.
- Link to v1: https://lore.kernel.org/r/20260919-tda54-upstream-i2c-v1-0-b0b9f77be18b@ti.com
---
Aniket Limaye (3):
dt-bindings: i2c: dw: Add ti,tda54-i2c compatible
i2c: designware: Support per-variant reg/bit offsets and intr handling
i2c: designware: Add ti,tda54-i2c support
.../bindings/i2c/snps,designware-i2c.yaml | 33 ++++
drivers/gpu/drm/xe/regs/xe_i2c_regs.h | 3 +-
drivers/i2c/busses/i2c-designware-amdisp.c | 1 +
drivers/i2c/busses/i2c-designware-common.c | 193 ++++++++++++++++++---
drivers/i2c/busses/i2c-designware-core.h | 70 +++++++-
drivers/i2c/busses/i2c-designware-master.c | 78 +++++----
drivers/i2c/busses/i2c-designware-pcidrv.c | 2 +
drivers/i2c/busses/i2c-designware-platdrv.c | 3 +
drivers/i2c/busses/i2c-designware-slave.c | 26 +--
include/linux/designware_i2c.h | 137 ++++++++++-----
10 files changed, 432 insertions(+), 114 deletions(-)
---
base-commit: 587858367581b9c55c3690f4e63382ad622719d4
change-id: 20260919-tda54-upstream-i2c-d0c67f16b4fc
Best regards,
--
Aniket Limaye <a-limaye@ti.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 1/3] dt-bindings: i2c: dw: Add ti,tda54-i2c compatible
2026-09-25 6:56 [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Aniket Limaye
@ 2026-09-25 6:56 ` Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 2/3] i2c: designware: Support per-variant reg/bit offsets and intr handling Aniket Limaye
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Aniket Limaye @ 2026-09-25 6:56 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Andy Shevchenko,
Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter
Cc: linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel,
Aniket Limaye
Add the "ti,tda54-i2c" compatible for Synopsys Advanced I2C Controller
(DWC_i2c) present on TDA54 SoC. Compared to the existing
"snps,designware-i2c" (DW_apb_i2c), it broadly differs in its register
offsets and some bit fields, requiring changes to interrupt handling,
timing handling, etc.
Unlike DW_apb_i2c, this IP has no COMP_PARAM_1 register to autodetect
the FIFO depth from, so add snps,tx-fifo-depth and
snps,rx-fifo-depth as required properties.
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
Changes in v2:
- Rename snps,dwc-i2c to ti,tda54-i2c
- Link to v1: https://lore.kernel.org/r/20260919-tda54-upstream-i2c-v1-1-b0b9f77be18b@ti.com/
---
.../bindings/i2c/snps,designware-i2c.yaml | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
index 467bdcbb8538..80a9376d6de7 100644
--- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml
@@ -21,12 +21,23 @@ allOf:
properties:
reg:
maxItems: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,tda54-i2c
+ then:
+ required:
+ - snps,tx-fifo-depth
+ - snps,rx-fifo-depth
properties:
compatible:
oneOf:
- description: Generic Synopsys DesignWare I2C controller
const: snps,designware-i2c
+ - description: TI TDA54 SoC I2C controller (Synopsys DWC_i2c IP)
+ const: ti,tda54-i2c
- description: Renesas RZ/N1D I2C controller
items:
- const: renesas,r9a06g032-i2c # RZ/N1D
@@ -121,6 +132,18 @@ properties:
low period of SCL line.
type: boolean
+ snps,tx-fifo-depth:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The depth of the hardware TX FIFO. Required on the ti,tda54-i2c
+ variant, where fifo depth cannot be autodetected.
+
+ snps,rx-fifo-depth:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The depth of the hardware RX FIFO. Required on the ti,tda54-i2c
+ variant, where fifo depth cannot be autodetected.
+
unevaluatedProperties: false
required:
@@ -172,4 +195,14 @@ examples:
interrupts = <8>;
clocks = <&ahb_clk>;
};
+ - |
+ i2c@53b00000 {
+ compatible = "ti,tda54-i2c";
+ reg = <0x53b00000 0x1000>;
+ interrupts = <166>;
+ clocks = <&sysclk>;
+ clock-frequency = <100000>;
+ snps,tx-fifo-depth = <32>;
+ snps,rx-fifo-depth = <32>;
+ };
...
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/3] i2c: designware: Support per-variant reg/bit offsets and intr handling
2026-09-25 6:56 [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 1/3] dt-bindings: i2c: dw: Add ti,tda54-i2c compatible Aniket Limaye
@ 2026-09-25 6:56 ` Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 3/3] i2c: designware: Add ti,tda54-i2c support Aniket Limaye
2026-09-25 9:41 ` [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Andy Shevchenko
3 siblings, 0 replies; 8+ messages in thread
From: Aniket Limaye @ 2026-09-25 6:56 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Andy Shevchenko,
Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter
Cc: linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel,
Aniket Limaye
Every DW_IC_* register offset and CON-register bit position is currently
baked in as a compile-time constant, which only works while there is a
single register layout.
- Make DW_IC_* a logical enum (enum dw_ic_reg_id) and the regmap
internally translates to physical offsets.
- Add a struct to hold the CON-register bitfield positions.
- Fold the read-to-clear interrupt-acknowledgment pattern into a new
i2c_dw_ack_intr() helper, driven by a per-variant dev->intr_clr[].
All of these are selected at probe time via the new
i2c_dw_select_variant().
Only one variant exists at this point (DW_apb_i2c), so this is a
mechanical, behavior-preserving change. It lays the groundwork for
adding a second register layout without duplicating the whole driver.
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
Changes in v3:
- Revert to special handling for the max_register in regmap for
MODEL_AMD_NAVI_GPU instead of unconditionally including
AMD_UCSI_INTR_REG in the legal regmap range.
- Link to v2: https://lore.kernel.org/all/20260925-tda54-upstream-i2c-v2-2-261785e58493@ti.com/
Changes in v2:
- Move the Reg ID -> Offset mapping inside the regmap functions such
that existing calls to reg_read/write are unaltered. This does change
the reg_read()/reg_write() functions to take reg IDs as input rather
than their physical offsets, which also requires updates to the
xe_i2c.c driver to handle the same mapping.
- Link to v1: https://lore.kernel.org/r/20260919-tda54-upstream-i2c-v1-2-b0b9f77be18b@ti.com/
---
drivers/gpu/drm/xe/regs/xe_i2c_regs.h | 3 +-
drivers/i2c/busses/i2c-designware-amdisp.c | 1 +
drivers/i2c/busses/i2c-designware-common.c | 71 +++++++++++---
drivers/i2c/busses/i2c-designware-core.h | 52 ++++++++++-
drivers/i2c/busses/i2c-designware-master.c | 54 +++++------
drivers/i2c/busses/i2c-designware-pcidrv.c | 2 +
drivers/i2c/busses/i2c-designware-platdrv.c | 2 +
drivers/i2c/busses/i2c-designware-slave.c | 26 +++---
include/linux/designware_i2c.h | 137 +++++++++++++++++++---------
9 files changed, 252 insertions(+), 96 deletions(-)
diff --git a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
index 37550e4a20f8..86449efb4a98 100644
--- a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
@@ -2,6 +2,7 @@
#ifndef _XE_I2C_REGS_H_
#define _XE_I2C_REGS_H_
+#include <linux/designware_i2c.h>
#include <linux/pci_regs.h>
#include "xe_reg_defs.h"
@@ -20,6 +21,6 @@
#define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND)
#define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84)
-#define I2C_REG(reg) XE_REG((reg) + I2C_MEM_SPACE_OFFSET)
+#define I2C_REG(reg) XE_REG(dw_i2c_reg_offsets[reg] + I2C_MEM_SPACE_OFFSET)
#endif /* _XE_I2C_REGS_H_ */
diff --git a/drivers/i2c/busses/i2c-designware-amdisp.c b/drivers/i2c/busses/i2c-designware-amdisp.c
index 9f0ec0fae6f2..f7aa075c9977 100644
--- a/drivers/i2c/busses/i2c-designware-amdisp.c
+++ b/drivers/i2c/busses/i2c-designware-amdisp.c
@@ -46,6 +46,7 @@ static int amd_isp_dw_i2c_plat_probe(struct platform_device *pdev)
isp_i2c_dev->flags |= ACCESS_POLLING;
platform_set_drvdata(pdev, isp_i2c_dev);
+ i2c_dw_select_variant(isp_i2c_dev);
isp_i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(isp_i2c_dev->base))
return dev_err_probe(&pdev->dev, PTR_ERR(isp_i2c_dev->base),
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index a1eca6cd4b75..bd25b9c582c1 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -72,11 +72,55 @@ static const char *const abort_sources[] = {
"incorrect slave-transmitter mode configuration",
};
+static const struct dw_i2c_con_bits dw_i2c_con_bits = {
+ .master = DW_IC_CON_MASTER,
+ .speed_std = DW_IC_CON_SPEED_STD,
+ .speed_fast = DW_IC_CON_SPEED_FAST,
+ .speed_high = DW_IC_CON_SPEED_HIGH,
+ .speed_mask = DW_IC_CON_SPEED_MASK,
+ .bit10_slave = DW_IC_CON_10BITADDR_SLAVE,
+ .bit10_master = DW_IC_CON_10BITADDR_MASTER,
+ .restart_en = DW_IC_CON_RESTART_EN,
+ .slave_disable = DW_IC_CON_SLAVE_DISABLE,
+ .stop_det_ifaddressed = DW_IC_CON_STOP_DET_IFADDRESSED,
+ .tx_empty_ctrl = DW_IC_CON_TX_EMPTY_CTRL,
+ .rx_fifo_full_hld_ctrl = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL,
+ .bus_clear_ctrl = DW_IC_CON_BUS_CLEAR_CTRL,
+};
+
+/* "snps,designware-i2c": dedicated read-to-clear register ID per logical interrupt */
+static const u32 dw_i2c_intr_clr[DW_INTR_IDX_MAX] = {
+ [DW_INTR_IDX_INTR] = DW_IC_CLR_INTR,
+ [DW_INTR_IDX_RX_UNDER] = DW_IC_CLR_RX_UNDER,
+ [DW_INTR_IDX_RX_OVER] = DW_IC_CLR_RX_OVER,
+ [DW_INTR_IDX_TX_OVER] = DW_IC_CLR_TX_OVER,
+ [DW_INTR_IDX_RD_REQ] = DW_IC_CLR_RD_REQ,
+ [DW_INTR_IDX_TX_ABRT] = DW_IC_CLR_TX_ABRT,
+ [DW_INTR_IDX_RX_DONE] = DW_IC_CLR_RX_DONE,
+ [DW_INTR_IDX_ACTIVITY] = DW_IC_CLR_ACTIVITY,
+ [DW_INTR_IDX_STOP_DET] = DW_IC_CLR_STOP_DET,
+ [DW_INTR_IDX_START_DET] = DW_IC_CLR_START_DET,
+ [DW_INTR_IDX_GEN_CALL] = DW_IC_CLR_GEN_CALL,
+};
+
+/**
+ * i2c_dw_select_variant() - Pick the register offset table, CON-register bit
+ * layout and interrupt-ack mapping matching this device's IP variant
+ * @dev: device private data
+ */
+void i2c_dw_select_variant(struct dw_i2c_dev *dev)
+{
+ dev->regs = dw_i2c_reg_offsets;
+ dev->con_bits = &dw_i2c_con_bits;
+ dev->intr_clr = dw_i2c_intr_clr;
+}
+EXPORT_SYMBOL_GPL(i2c_dw_select_variant);
+
static int dw_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct dw_i2c_dev *dev = context;
- *val = readl(dev->base + reg);
+ *val = readl(dev->base + dev->regs[reg]);
return 0;
}
@@ -85,7 +129,7 @@ static int dw_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct dw_i2c_dev *dev = context;
- writel(val, dev->base + reg);
+ writel(val, dev->base + dev->regs[reg]);
return 0;
}
@@ -94,7 +138,7 @@ static int dw_reg_read_swab(void *context, unsigned int reg, unsigned int *val)
{
struct dw_i2c_dev *dev = context;
- *val = swab32(readl(dev->base + reg));
+ *val = swab32(readl(dev->base + dev->regs[reg]));
return 0;
}
@@ -103,7 +147,7 @@ static int dw_reg_write_swab(void *context, unsigned int reg, unsigned int val)
{
struct dw_i2c_dev *dev = context;
- writel(swab32(val), dev->base + reg);
+ writel(swab32(val), dev->base + dev->regs[reg]);
return 0;
}
@@ -111,9 +155,10 @@ static int dw_reg_write_swab(void *context, unsigned int reg, unsigned int val)
static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val)
{
struct dw_i2c_dev *dev = context;
+ unsigned int offset = dev->regs[reg];
- *val = readw(dev->base + reg) |
- (readw(dev->base + reg + DW_IC_REG_STEP_BYTES) << DW_IC_REG_WORD_SHIFT);
+ *val = readw(dev->base + offset) |
+ (readw(dev->base + offset + DW_IC_REG_STEP_BYTES) << DW_IC_REG_WORD_SHIFT);
return 0;
}
@@ -121,9 +166,10 @@ static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val)
static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val)
{
struct dw_i2c_dev *dev = context;
+ unsigned int offset = dev->regs[reg];
- writew(val, dev->base + reg);
- writew(val >> DW_IC_REG_WORD_SHIFT, dev->base + reg + DW_IC_REG_STEP_BYTES);
+ writew(val, dev->base + offset);
+ writew(val >> DW_IC_REG_WORD_SHIFT, dev->base + offset + DW_IC_REG_STEP_BYTES);
return 0;
}
@@ -143,7 +189,7 @@ static int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
struct regmap_config map_cfg = {
.reg_bits = 32,
.val_bits = 32,
- .reg_stride = 4,
+ .reg_stride = 1,
.disable_locking = true,
.reg_read = dw_reg_read,
.reg_write = dw_reg_write,
@@ -163,7 +209,7 @@ static int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
if (ret)
return ret;
- reg = readl(dev->base + DW_IC_COMP_TYPE);
+ reg = readl(dev->base + dev->regs[DW_IC_COMP_TYPE]);
i2c_dw_release_lock(dev);
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
@@ -845,7 +891,6 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
- unsigned int dummy;
int ret;
ret = i2c_dw_acquire_lock(dev);
@@ -857,7 +902,7 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
/* Disable all interrupts */
__i2c_dw_write_intr_mask(dev, 0);
- regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_INTR);
i2c_dw_release_lock(dev);
}
@@ -1055,7 +1100,7 @@ void i2c_dw_shutdown(struct dw_i2c_dev *dev)
* disable bit while the controller is still enabled.
*/
regmap_read(dev->map, DW_IC_CON, &con);
- con |= DW_IC_CON_SLAVE_DISABLE;
+ con |= dev->con_bits->slave_disable;
regmap_write(dev->map, DW_IC_CON, con);
i2c_dw_disable(dev);
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 2c929a6e8da2..4c782594ab4e 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -43,6 +43,42 @@
#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */
#define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */
+/*
+ * Bit positions within the CON register that could differ between IP
+ * variants.
+ */
+struct dw_i2c_con_bits {
+ u32 master;
+ u32 speed_std;
+ u32 speed_fast;
+ u32 speed_high;
+ u32 speed_mask;
+ u32 bit10_slave;
+ u32 bit10_master;
+ u32 restart_en;
+ u32 slave_disable;
+ u32 stop_det_ifaddressed;
+ u32 tx_empty_ctrl;
+ u32 rx_fifo_full_hld_ctrl;
+ u32 bus_clear_ctrl;
+};
+
+/* Logical interrupt IDs for i2c_dw_ack_intr() */
+enum dw_i2c_intr_idx {
+ DW_INTR_IDX_INTR,
+ DW_INTR_IDX_RX_UNDER,
+ DW_INTR_IDX_RX_OVER,
+ DW_INTR_IDX_TX_OVER,
+ DW_INTR_IDX_RD_REQ,
+ DW_INTR_IDX_TX_ABRT,
+ DW_INTR_IDX_RX_DONE,
+ DW_INTR_IDX_ACTIVITY,
+ DW_INTR_IDX_STOP_DET,
+ DW_INTR_IDX_START_DET,
+ DW_INTR_IDX_GEN_CALL,
+ DW_INTR_IDX_MAX,
+};
+
#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
DW_IC_INTR_TX_ABRT | \
DW_IC_INTR_STOP_DET)
@@ -126,6 +162,9 @@ struct reset_control;
* struct dw_i2c_dev - private i2c-designware data
* @dev: driver model device node
* @map: IO registers map
+ * @regs: logical-to-physical register offset table for the active IP variant
+ * @con_bits: CON register bit-layout for the active IP variant
+ * @intr_clr: logical intr number to reg table for the active IP variant
* @sysmap: System controller registers map
* @base: IO registers pointer
* @ext: Extended IO registers pointer
@@ -189,6 +228,9 @@ struct reset_control;
struct dw_i2c_dev {
struct device *dev;
struct regmap *map;
+ const u32 *regs;
+ const struct dw_i2c_con_bits *con_bits;
+ const u32 *intr_clr;
struct regmap *sysmap;
void __iomem *base;
void __iomem *ext;
@@ -255,7 +297,6 @@ struct dw_i2c_dev {
* Enable UCSI interrupt by writing 0xd at register
* offset 0x474 specified in hardware specification.
*/
-#define AMD_UCSI_INTR_REG 0x474
#define AMD_UCSI_INTR_EN 0xd
#define TXGBE_TX_FIFO_DEPTH 4
@@ -265,6 +306,7 @@ struct i2c_dw_semaphore_callbacks {
int (*probe)(struct dw_i2c_dev *dev);
};
+void i2c_dw_select_variant(struct dw_i2c_dev *dev);
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
u32 tSYMBOL, u32 tf, int offset);
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
@@ -310,6 +352,14 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev,
*intr_mask = dev->sw_mask;
}
+/* Acknowledge a logical interrupt via dev->intr_clr[]: reg ID */
+static inline void i2c_dw_ack_intr(struct dw_i2c_dev *dev, enum dw_i2c_intr_idx intr)
+{
+ unsigned int dummy;
+
+ regmap_read(dev->map, dev->intr_clr[intr], &dummy);
+}
+
void __i2c_dw_disable(struct dw_i2c_dev *dev);
void i2c_dw_disable(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index a1bcc3797e4f..329f958ded3e 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -133,14 +133,14 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
fp_str, dev->fs_hcnt, dev->fs_lcnt);
/* Check is high speed possible and fall back to fast mode if not */
- if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) ==
- DW_IC_CON_SPEED_HIGH) {
+ if ((dev->master_cfg & dev->con_bits->speed_mask) ==
+ dev->con_bits->speed_high) {
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
dev_err(dev->dev, "High Speed not supported!\n");
t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
- dev->master_cfg &= ~DW_IC_CON_SPEED_MASK;
- dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ dev->master_cfg &= ~dev->con_bits->speed_mask;
+ dev->master_cfg |= dev->con_bits->speed_fast;
dev->hs_hcnt = 0;
dev->hs_lcnt = 0;
} else if (!dev->hs_hcnt || !dev->hs_lcnt) {
@@ -200,7 +200,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* If the slave address is ten bit address, enable 10BITADDR */
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
- ic_con = DW_IC_CON_10BITADDR_MASTER;
+ ic_con = dev->con_bits->bit10_master;
/*
* If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
* mode has to be enabled via bit 12 of IC_TAR register.
@@ -210,7 +210,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
ic_tar = DW_IC_TAR_10BITADDR_MASTER;
}
- regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
+ regmap_update_bits(dev->map, DW_IC_CON, dev->con_bits->bit10_master,
ic_con);
/*
@@ -230,7 +230,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
regmap_read(dev->map, DW_IC_ENABLE_STATUS, &dummy);
/* Clear and enable interrupts */
- regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_INTR);
__i2c_dw_write_intr_mask(dev, DW_IC_INTR_MASTER_MASK);
}
@@ -399,8 +399,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
* IC_RESTART_EN are set, we must manually
* set restart bit between messages.
*/
- if ((dev->master_cfg & DW_IC_CON_RESTART_EN) &&
- (dev->msg_write_idx > 0))
+ if (dev->master_cfg & dev->con_bits->restart_en &&
+ dev->msg_write_idx > 0)
need_restart = true;
}
@@ -574,7 +574,7 @@ i2c_dw_read(struct dw_i2c_dev *dev)
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
- unsigned int stat, dummy;
+ unsigned int stat;
/*
* The IC_INTR_STAT register just indicates "enabled" interrupts.
@@ -603,32 +603,32 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_RX_UNDER)
- regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_UNDER);
if (stat & DW_IC_INTR_RX_OVER)
- regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
- regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_TX_OVER);
if (stat & DW_IC_INTR_RD_REQ)
- regmap_read(dev->map, DW_IC_CLR_RD_REQ, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RD_REQ);
if (stat & DW_IC_INTR_TX_ABRT) {
/*
* The IC_TX_ABRT_SOURCE register is cleared whenever
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
*/
regmap_read(dev->map, DW_IC_TX_ABRT_SOURCE, &dev->abort_source);
- regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_TX_ABRT);
}
if (stat & DW_IC_INTR_RX_DONE)
- regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_DONE);
if (stat & DW_IC_INTR_ACTIVITY)
- regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_ACTIVITY);
if ((stat & DW_IC_INTR_STOP_DET) &&
((dev->rx_outstanding == 0) || (stat & DW_IC_INTR_RX_FULL)))
- regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_STOP_DET);
if (stat & DW_IC_INTR_START_DET)
- regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_START_DET);
if (stat & DW_IC_INTR_GEN_CALL)
- regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_GEN_CALL);
return stat;
}
@@ -940,20 +940,20 @@ void i2c_dw_configure_master(struct dw_i2c_dev *dev)
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;
+ dev->master_cfg = dev->con_bits->master | dev->con_bits->slave_disable |
+ dev->con_bits->restart_en;
dev->mode = DW_IC_MASTER;
switch (t->bus_freq_hz) {
case I2C_MAX_STANDARD_MODE_FREQ:
- dev->master_cfg |= DW_IC_CON_SPEED_STD;
+ dev->master_cfg |= dev->con_bits->speed_std;
break;
case I2C_MAX_HIGH_SPEED_MODE_FREQ:
- dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
+ dev->master_cfg |= dev->con_bits->speed_high;
break;
default:
- dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ dev->master_cfg |= dev->con_bits->speed_fast;
}
}
EXPORT_SYMBOL_GPL(i2c_dw_configure_master);
@@ -1042,8 +1042,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
if (ret)
return ret;
- if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL)
- dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL;
+ if (ic_con & dev->con_bits->bus_clear_ctrl)
+ dev->master_cfg |= dev->con_bits->bus_clear_ctrl;
return i2c_dw_init_recovery_info(dev);
}
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index 468287922363..fc73106fb0f9 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -246,6 +246,8 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, dev);
+ i2c_dw_select_variant(dev);
+
if (controller->setup) {
r = controller->setup(pdev, controller);
if (r)
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 447af5523c2e..42b34c678146 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -156,6 +156,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
dev->flags = flags;
platform_set_drvdata(pdev, dev);
+ i2c_dw_select_variant(dev);
+
ret = dw_i2c_plat_request_regs(dev);
if (ret)
return ret;
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index 0abcc7757b23..0985e907c9c3 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -65,7 +65,7 @@ int i2c_dw_unreg_slave(struct i2c_client *slave)
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
{
- unsigned int stat, dummy;
+ unsigned int stat;
/*
* The IC_INTR_STAT register just indicates "enabled" interrupts.
@@ -89,23 +89,23 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_TX_ABRT)
- regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_TX_ABRT);
if (stat & DW_IC_INTR_RX_UNDER)
- regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_UNDER);
if (stat & DW_IC_INTR_RX_OVER)
- regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
- regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_TX_OVER);
if (stat & DW_IC_INTR_RX_DONE)
- regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RX_DONE);
if (stat & DW_IC_INTR_ACTIVITY)
- regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_ACTIVITY);
if (stat & DW_IC_INTR_STOP_DET)
- regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_STOP_DET);
if (stat & DW_IC_INTR_START_DET)
- regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_START_DET);
if (stat & DW_IC_INTR_GEN_CALL)
- regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_GEN_CALL);
return stat;
}
@@ -155,7 +155,7 @@ irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev)
if (stat & DW_IC_INTR_RD_REQ) {
if (slave_activity) {
- regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
+ i2c_dw_ack_intr(dev, DW_INTR_IDX_RD_REQ);
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
i2c_slave_event(dev->slave,
@@ -185,8 +185,8 @@ void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
dev->functionality |= I2C_FUNC_SLAVE;
- dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
- DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
+ dev->slave_cfg = dev->con_bits->rx_fifo_full_hld_ctrl |
+ dev->con_bits->restart_en | dev->con_bits->stop_det_ifaddressed;
}
EXPORT_SYMBOL_GPL(i2c_dw_configure_slave);
diff --git a/include/linux/designware_i2c.h b/include/linux/designware_i2c.h
index 53f37f18a722..994e0e5ea623 100644
--- a/include/linux/designware_i2c.h
+++ b/include/linux/designware_i2c.h
@@ -11,48 +11,103 @@
#include <linux/bits.h>
/*
- * Registers offset
+ * Logical register IDs. Each ID is resolved to a physical offset via a
+ * per-variant table (see dw_i2c_reg_offsets[] below); regmap backends
+ * (dw_reg_read()/dw_reg_write() and the Xe I2C_REG() macro) perform that
+ * lookup so callers never need to know the real offset.
*/
-#define DW_IC_CON 0x00
-#define DW_IC_TAR 0x04
-#define DW_IC_SAR 0x08
-#define DW_IC_DATA_CMD 0x10
-#define DW_IC_SS_SCL_HCNT 0x14
-#define DW_IC_SS_SCL_LCNT 0x18
-#define DW_IC_FS_SCL_HCNT 0x1c
-#define DW_IC_FS_SCL_LCNT 0x20
-#define DW_IC_HS_SCL_HCNT 0x24
-#define DW_IC_HS_SCL_LCNT 0x28
-#define DW_IC_INTR_STAT 0x2c
-#define DW_IC_INTR_MASK 0x30
-#define DW_IC_RAW_INTR_STAT 0x34
-#define DW_IC_RX_TL 0x38
-#define DW_IC_TX_TL 0x3c
-#define DW_IC_CLR_INTR 0x40
-#define DW_IC_CLR_RX_UNDER 0x44
-#define DW_IC_CLR_RX_OVER 0x48
-#define DW_IC_CLR_TX_OVER 0x4c
-#define DW_IC_CLR_RD_REQ 0x50
-#define DW_IC_CLR_TX_ABRT 0x54
-#define DW_IC_CLR_RX_DONE 0x58
-#define DW_IC_CLR_ACTIVITY 0x5c
-#define DW_IC_CLR_STOP_DET 0x60
-#define DW_IC_CLR_START_DET 0x64
-#define DW_IC_CLR_GEN_CALL 0x68
-#define DW_IC_ENABLE 0x6c
-#define DW_IC_STATUS 0x70
-#define DW_IC_TXFLR 0x74
-#define DW_IC_RXFLR 0x78
-#define DW_IC_SDA_HOLD 0x7c
-#define DW_IC_TX_ABRT_SOURCE 0x80
-#define DW_IC_ENABLE_STATUS 0x9c
-#define DW_IC_CLR_RESTART_DET 0xa8
-#define DW_IC_SMBUS_INTR_STAT 0xc8
-#define DW_IC_SMBUS_INTR_MASK 0xcc
-#define DW_IC_CLR_SMBUS_INTR 0xd4
-#define DW_IC_COMP_PARAM_1 0xf4
-#define DW_IC_COMP_VERSION 0xf8
-#define DW_IC_COMP_TYPE 0xfc
+enum dw_ic_reg_id {
+ DW_IC_CON,
+ DW_IC_TAR,
+ DW_IC_SAR,
+ DW_IC_DATA_CMD,
+ DW_IC_SS_SCL_HCNT,
+ DW_IC_SS_SCL_LCNT,
+ DW_IC_FS_SCL_HCNT,
+ DW_IC_FS_SCL_LCNT,
+ DW_IC_HS_SCL_HCNT,
+ DW_IC_HS_SCL_LCNT,
+ DW_IC_INTR_STAT,
+ DW_IC_INTR_MASK,
+ DW_IC_RAW_INTR_STAT,
+ DW_IC_RX_TL,
+ DW_IC_TX_TL,
+ DW_IC_CLR_INTR,
+ DW_IC_CLR_RX_UNDER,
+ DW_IC_CLR_RX_OVER,
+ DW_IC_CLR_TX_OVER,
+ DW_IC_CLR_RD_REQ,
+ DW_IC_CLR_TX_ABRT,
+ DW_IC_CLR_RX_DONE,
+ DW_IC_CLR_ACTIVITY,
+ DW_IC_CLR_STOP_DET,
+ DW_IC_CLR_START_DET,
+ DW_IC_CLR_GEN_CALL,
+ DW_IC_ENABLE,
+ DW_IC_STATUS,
+ DW_IC_TXFLR,
+ DW_IC_RXFLR,
+ DW_IC_SDA_HOLD,
+ DW_IC_TX_ABRT_SOURCE,
+ DW_IC_ENABLE_STATUS,
+ DW_IC_CLR_RESTART_DET,
+ DW_IC_SMBUS_INTR_STAT,
+ DW_IC_SMBUS_INTR_MASK,
+ DW_IC_CLR_SMBUS_INTR,
+ DW_IC_COMP_PARAM_1,
+ DW_IC_COMP_VERSION,
+ DW_IC_COMP_TYPE,
+ AMD_UCSI_INTR_REG,
+ DW_IC_REG_MAX
+};
+
+/*
+ * Default "snps,designware-i2c" register offsets, indexed by
+ * enum dw_ic_reg_id.
+ */
+static const __maybe_unused u32 dw_i2c_reg_offsets[DW_IC_REG_MAX] = {
+ [DW_IC_CON] = 0x00,
+ [DW_IC_TAR] = 0x04,
+ [DW_IC_SAR] = 0x08,
+ [DW_IC_DATA_CMD] = 0x10,
+ [DW_IC_SS_SCL_HCNT] = 0x14,
+ [DW_IC_SS_SCL_LCNT] = 0x18,
+ [DW_IC_FS_SCL_HCNT] = 0x1c,
+ [DW_IC_FS_SCL_LCNT] = 0x20,
+ [DW_IC_HS_SCL_HCNT] = 0x24,
+ [DW_IC_HS_SCL_LCNT] = 0x28,
+ [DW_IC_INTR_STAT] = 0x2c,
+ [DW_IC_INTR_MASK] = 0x30,
+ [DW_IC_RAW_INTR_STAT] = 0x34,
+ [DW_IC_RX_TL] = 0x38,
+ [DW_IC_TX_TL] = 0x3c,
+ [DW_IC_CLR_INTR] = 0x40,
+ [DW_IC_CLR_RX_UNDER] = 0x44,
+ [DW_IC_CLR_RX_OVER] = 0x48,
+ [DW_IC_CLR_TX_OVER] = 0x4c,
+ [DW_IC_CLR_RD_REQ] = 0x50,
+ [DW_IC_CLR_TX_ABRT] = 0x54,
+ [DW_IC_CLR_RX_DONE] = 0x58,
+ [DW_IC_CLR_ACTIVITY] = 0x5c,
+ [DW_IC_CLR_STOP_DET] = 0x60,
+ [DW_IC_CLR_START_DET] = 0x64,
+ [DW_IC_CLR_GEN_CALL] = 0x68,
+ [DW_IC_ENABLE] = 0x6c,
+ [DW_IC_STATUS] = 0x70,
+ [DW_IC_TXFLR] = 0x74,
+ [DW_IC_RXFLR] = 0x78,
+ [DW_IC_SDA_HOLD] = 0x7c,
+ [DW_IC_TX_ABRT_SOURCE] = 0x80,
+ [DW_IC_ENABLE_STATUS] = 0x9c,
+ [DW_IC_CLR_RESTART_DET] = 0xa8,
+ [DW_IC_SMBUS_INTR_STAT] = 0xc8,
+ [DW_IC_SMBUS_INTR_MASK] = 0xcc,
+ [DW_IC_CLR_SMBUS_INTR] = 0xd4,
+ [DW_IC_COMP_PARAM_1] = 0xf4,
+ [DW_IC_COMP_VERSION] = 0xf8,
+ [DW_IC_COMP_TYPE] = 0xfc,
+ [AMD_UCSI_INTR_REG] = 0x474,
+};
/* DW_IC_CON bits */
#define DW_IC_CON_MASTER BIT(0)
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/3] i2c: designware: Add ti,tda54-i2c support
2026-09-25 6:56 [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 1/3] dt-bindings: i2c: dw: Add ti,tda54-i2c compatible Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 2/3] i2c: designware: Support per-variant reg/bit offsets and intr handling Aniket Limaye
@ 2026-09-25 6:56 ` Aniket Limaye
2026-09-25 9:41 ` [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Andy Shevchenko
3 siblings, 0 replies; 8+ messages in thread
From: Aniket Limaye @ 2026-09-25 6:56 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Andy Shevchenko,
Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter
Cc: linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel,
Aniket Limaye
Compared to the existing DW_apb_i2c layout [1], DWC_i2c present in TDA54
SoC [0] differs enough to need MODEL_TI_TDA54-gated special-casing in a
few places:
- Add new register offsets table for TI TDA54 SoC.
- The DW_IC_CON register bitfields are also updated.
- Interrupts are acknowledged by writing a bitmask to a single CLR_INTR
register instead of reading N dedicated CLR_* registers;
i2c_dw_ack_intr() picks the right method based on dev->flags.
- One HCNT/LCNT register pair is shared between standard and fast speed
instead of having one pair each; i2c_dw_write_timings() writes
whichever value set matches the configured speed.
- No COMP_PARAM_1 register, so FIFO depth and high-speed-mode support
can't be autodetected: FIFO depth now comes from the required
snps,tx-fifo-depth/snps,rx-fifo-depth DT properties, and the
high-speed capability check is skipped.
- No defined CON.RESTART_EN bit; treated as always set.
- TDA54 SoC does not have SMBus block, skip accesses to those registers.
[0]: DWC_i2c_reference.pdf
[1]: DW_apb_i2c_databook.pdf
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
Changes in v2:
- Rename compatible/flag: snps,dwc-i2c -> ti,tda54-i2c,
MODEL_DWC_I2C -> MODEL_TI_TDA54.
- Remove i2c_dwc_has_smbus(); just skip the SMBUS_INTR_MASK write under
MODEL_TI_TDA54.
- Link to v1: https://lore.kernel.org/r/20260919-tda54-upstream-i2c-v1-3-b0b9f77be18b@ti.com/
---
drivers/i2c/busses/i2c-designware-common.c | 128 +++++++++++++++++++++++++---
drivers/i2c/busses/i2c-designware-core.h | 22 ++++-
drivers/i2c/busses/i2c-designware-master.c | 26 +++---
drivers/i2c/busses/i2c-designware-platdrv.c | 1 +
4 files changed, 153 insertions(+), 24 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index bd25b9c582c1..8cb162a6aed8 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -72,6 +72,40 @@ static const char *const abort_sources[] = {
"incorrect slave-transmitter mode configuration",
};
+/* "ti,tda54-i2c" block-based register layout */
+#define DWC_ADV_OPERATION_BLK_OFFSET 0x00
+#define DWC_ADV_I2C_BLK_OFFSET 0x28
+#define DWC_ADV_FIFO_INTR_BLK_OFFSET 0xbc
+#define DWC_ADV_DEBUG_BLK_OFFSET 0xf8
+
+static const u32 tda54_i2c_reg_offsets[DW_IC_REG_MAX] = {
+ [DW_IC_ENABLE] = DWC_ADV_OPERATION_BLK_OFFSET + 0x04,
+ [DW_IC_CON] = DWC_ADV_I2C_BLK_OFFSET + 0x04,
+ [DW_IC_TAR] = DWC_ADV_I2C_BLK_OFFSET + 0x08,
+ [DW_IC_SAR] = DWC_ADV_I2C_BLK_OFFSET + 0x0c,
+ [DW_IC_DATA_CMD] = DWC_ADV_I2C_BLK_OFFSET + 0x58,
+ [DW_IC_SS_SCL_HCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x24, /* shared SS/FS pair */
+ [DW_IC_SS_SCL_LCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x28,
+ [DW_IC_FS_SCL_HCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x24,
+ [DW_IC_FS_SCL_LCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x28,
+ [DW_IC_HS_SCL_HCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x2c,
+ [DW_IC_HS_SCL_LCNT] = DWC_ADV_I2C_BLK_OFFSET + 0x30,
+ [DW_IC_SDA_HOLD] = DWC_ADV_I2C_BLK_OFFSET + 0x34,
+ [DW_IC_RX_TL] = DWC_ADV_I2C_BLK_OFFSET + 0x5c,
+ [DW_IC_TX_TL] = DWC_ADV_I2C_BLK_OFFSET + 0x60,
+ [DW_IC_INTR_STAT] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x04,
+ [DW_IC_INTR_MASK] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x08,
+ [DW_IC_RAW_INTR_STAT] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x0c,
+ [DW_IC_CLR_INTR] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x10,
+ [DW_IC_STATUS] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x1c,
+ [DW_IC_TXFLR] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x20,
+ [DW_IC_RXFLR] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x24,
+ [DW_IC_TX_ABRT_SOURCE] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x18,
+ [DW_IC_ENABLE_STATUS] = DWC_ADV_FIFO_INTR_BLK_OFFSET + 0x14,
+ [DW_IC_COMP_VERSION] = DWC_ADV_DEBUG_BLK_OFFSET + 0x08,
+ [DW_IC_COMP_TYPE] = DWC_ADV_DEBUG_BLK_OFFSET + 0x0c,
+};
+
static const struct dw_i2c_con_bits dw_i2c_con_bits = {
.master = DW_IC_CON_MASTER,
.speed_std = DW_IC_CON_SPEED_STD,
@@ -88,6 +122,26 @@ static const struct dw_i2c_con_bits dw_i2c_con_bits = {
.bus_clear_ctrl = DW_IC_CON_BUS_CLEAR_CTRL,
};
+/*
+ * DWC_IC_CTRL bit layout for "ti,tda54-i2c".
+ * There is no defined bit for RESTART_EN or SLAVE_DISABLE on this IP.
+ */
+static const struct dw_i2c_con_bits dwc_adv_i2c_con_bits = {
+ .master = BIT(0),
+ .speed_std = (1 << 4),
+ .speed_fast = (2 << 4),
+ .speed_high = (3 << 4),
+ .speed_mask = GENMASK(5, 4),
+ .bit10_slave = BIT(8),
+ .bit10_master = BIT(9),
+ .restart_en = 0,
+ .slave_disable = 0,
+ .stop_det_ifaddressed = BIT(10),
+ .tx_empty_ctrl = BIT(11),
+ .rx_fifo_full_hld_ctrl = BIT(12),
+ .bus_clear_ctrl = 0,
+};
+
/* "snps,designware-i2c": dedicated read-to-clear register ID per logical interrupt */
static const u32 dw_i2c_intr_clr[DW_INTR_IDX_MAX] = {
[DW_INTR_IDX_INTR] = DW_IC_CLR_INTR,
@@ -103,6 +157,21 @@ static const u32 dw_i2c_intr_clr[DW_INTR_IDX_MAX] = {
[DW_INTR_IDX_GEN_CALL] = DW_IC_CLR_GEN_CALL,
};
+/* "ti,tda54-i2c" CLR_INTR bit to write per logical interrupt */
+static const u32 dwc_adv_i2c_intr_clr[DW_INTR_IDX_MAX] = {
+ [DW_INTR_IDX_INTR] = DWC_ADV_IC_INTR_CLR_INTR,
+ [DW_INTR_IDX_RX_UNDER] = DWC_ADV_IC_INTR_CLR_RX_UNDER,
+ [DW_INTR_IDX_RX_OVER] = DWC_ADV_IC_INTR_CLR_RX_OVER,
+ [DW_INTR_IDX_TX_OVER] = DWC_ADV_IC_INTR_CLR_TX_OVER,
+ [DW_INTR_IDX_RD_REQ] = DWC_ADV_IC_INTR_CLR_RD_REQ,
+ [DW_INTR_IDX_TX_ABRT] = DWC_ADV_IC_INTR_CLR_TX_ABRT,
+ [DW_INTR_IDX_RX_DONE] = DWC_ADV_IC_INTR_CLR_RX_DONE,
+ [DW_INTR_IDX_ACTIVITY] = DWC_ADV_IC_INTR_CLR_ACTIVITY,
+ [DW_INTR_IDX_STOP_DET] = DWC_ADV_IC_INTR_CLR_STOP_DET,
+ [DW_INTR_IDX_START_DET] = DWC_ADV_IC_INTR_CLR_START_DET,
+ [DW_INTR_IDX_GEN_CALL] = DWC_ADV_IC_INTR_CLR_GEN_CALL,
+};
+
/**
* i2c_dw_select_variant() - Pick the register offset table, CON-register bit
* layout and interrupt-ack mapping matching this device's IP variant
@@ -110,9 +179,15 @@ static const u32 dw_i2c_intr_clr[DW_INTR_IDX_MAX] = {
*/
void i2c_dw_select_variant(struct dw_i2c_dev *dev)
{
- dev->regs = dw_i2c_reg_offsets;
- dev->con_bits = &dw_i2c_con_bits;
- dev->intr_clr = dw_i2c_intr_clr;
+ if (dev->flags & MODEL_TI_TDA54) {
+ dev->regs = tda54_i2c_reg_offsets;
+ dev->con_bits = &dwc_adv_i2c_con_bits;
+ dev->intr_clr = dwc_adv_i2c_intr_clr;
+ } else {
+ dev->regs = dw_i2c_reg_offsets;
+ dev->con_bits = &dw_i2c_con_bits;
+ dev->intr_clr = dw_i2c_intr_clr;
+ }
}
EXPORT_SYMBOL_GPL(i2c_dw_select_variant);
@@ -432,13 +507,27 @@ static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode)
static void i2c_dw_write_timings(struct dw_i2c_dev *dev)
{
- /* Write standard speed timing parameters */
- regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
- regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
-
- /* Write fast mode/fast mode plus timing parameters */
- regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
- regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
+ if (dev->flags & MODEL_TI_TDA54) {
+ /*
+ * Only one HCNT/LCNT register pair backs both speeds on
+ * this IP -- write whichever value set matches master_cfg.
+ */
+ if ((dev->master_cfg & dev->con_bits->speed_mask) == dev->con_bits->speed_std) {
+ regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
+ regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
+ } else {
+ regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
+ regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
+ }
+ } else {
+ /* Write standard speed timing parameters */
+ regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
+ regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
+
+ /* Write fast mode/fast mode plus timing parameters */
+ regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
+ regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
+ }
/* Write high speed timing parameters */
regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt);
@@ -493,8 +582,10 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
* Mask SMBus interrupts to block storms from broken
* firmware that leaves IC_SMBUS=1; the handler never
* services them.
+ * TDA54 SoC has no SMBus block, so skip it.
*/
- regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
+ if (!(dev->flags & MODEL_TI_TDA54))
+ regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
i2c_dw_write_timings(dev);
@@ -538,6 +629,14 @@ int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev)
if (device_property_read_u32(device, "snps,bus-capacitance-pf", &dev->bus_capacitance_pF))
dev->bus_capacitance_pF = DW_IC_DEFAULT_BUS_CAPACITANCE_pF;
+ if (dev->flags & MODEL_TI_TDA54) {
+ device_property_read_u32(device, "snps,tx-fifo-depth", &dev->tx_fifo_depth);
+ device_property_read_u32(device, "snps,rx-fifo-depth", &dev->rx_fifo_depth);
+
+ if (!dev->tx_fifo_depth || !dev->rx_fifo_depth)
+ return -EINVAL;
+ }
+
dev->clk_freq_optimized = device_property_read_bool(device, "snps,clk-freq-optimized");
/* Mobileye controllers do not hold the clock on empty FIFO */
@@ -854,6 +953,13 @@ static int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
return 0;
}
+ /*
+ * DW_IC_COMP_PARAM_1 not implemented on this IP;
+ * fifo depth set in i2c_dw_fw_parse_and_configure().
+ */
+ if (dev->flags & MODEL_TI_TDA54)
+ return 0;
+
/*
* Try to detect the FIFO depth if not set by interface driver,
* the depth could be from 2 to 256 from HW spec.
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 4c782594ab4e..bd96abf63c39 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -79,6 +79,18 @@ enum dw_i2c_intr_idx {
DW_INTR_IDX_MAX,
};
+#define DWC_ADV_IC_INTR_CLR_INTR BIT(0)
+#define DWC_ADV_IC_INTR_CLR_RX_UNDER BIT(1)
+#define DWC_ADV_IC_INTR_CLR_RX_OVER BIT(2)
+#define DWC_ADV_IC_INTR_CLR_TX_OVER BIT(3)
+#define DWC_ADV_IC_INTR_CLR_RD_REQ BIT(4)
+#define DWC_ADV_IC_INTR_CLR_TX_ABRT BIT(5)
+#define DWC_ADV_IC_INTR_CLR_RX_DONE BIT(6)
+#define DWC_ADV_IC_INTR_CLR_ACTIVITY BIT(7)
+#define DWC_ADV_IC_INTR_CLR_STOP_DET BIT(8)
+#define DWC_ADV_IC_INTR_CLR_START_DET BIT(9)
+#define DWC_ADV_IC_INTR_CLR_GEN_CALL BIT(10)
+
#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
DW_IC_INTR_TX_ABRT | \
DW_IC_INTR_STOP_DET)
@@ -291,7 +303,8 @@ struct dw_i2c_dev {
#define MODEL_AMD_NAVI_GPU BIT(10)
#define MODEL_WANGXUN_SP BIT(11)
-#define MODEL_MASK GENMASK(11, 8)
+#define MODEL_TI_TDA54 BIT(12)
+#define MODEL_MASK GENMASK(12, 8)
/*
* Enable UCSI interrupt by writing 0xd at register
@@ -352,12 +365,15 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev,
*intr_mask = dev->sw_mask;
}
-/* Acknowledge a logical interrupt via dev->intr_clr[]: reg ID */
+/* Acknowledge a logical interrupt via dev->intr_clr[]: reg ID or bit value */
static inline void i2c_dw_ack_intr(struct dw_i2c_dev *dev, enum dw_i2c_intr_idx intr)
{
unsigned int dummy;
- regmap_read(dev->map, dev->intr_clr[intr], &dummy);
+ if (dev->flags & MODEL_TI_TDA54)
+ regmap_write(dev->map, DW_IC_CLR_INTR, dev->intr_clr[intr]);
+ else
+ regmap_read(dev->map, dev->intr_clr[intr], &dummy);
}
void __i2c_dw_disable(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 329f958ded3e..94594f6349d4 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -42,14 +42,16 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
u32 ic_clk;
int ret;
- ret = i2c_dw_acquire_lock(dev);
- if (ret)
- return ret;
-
- ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &comp_param1);
- i2c_dw_release_lock(dev);
- if (ret)
- return ret;
+ if (!(dev->flags & MODEL_TI_TDA54)) {
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &comp_param1);
+ i2c_dw_release_lock(dev);
+ if (ret)
+ return ret;
+ }
/* Set standard and fast speed dividers for high/low periods */
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
@@ -135,7 +137,8 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
/* Check is high speed possible and fall back to fast mode if not */
if ((dev->master_cfg & dev->con_bits->speed_mask) ==
dev->con_bits->speed_high) {
- if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
+ if (!(dev->flags & MODEL_TI_TDA54) &&
+ (comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
dev_err(dev->dev, "High Speed not supported!\n");
t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
@@ -398,8 +401,11 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
* If both IC_EMPTYFIFO_HOLD_MASTER_EN and
* IC_RESTART_EN are set, we must manually
* set restart bit between messages.
+ * ti,tda54-i2c does not define a CON.RESTART_EN
+ * bit, and behaves like it is set to 1.
*/
- if (dev->master_cfg & dev->con_bits->restart_en &&
+ if (((dev->master_cfg & dev->con_bits->restart_en) ||
+ (dev->flags & MODEL_TI_TDA54)) &&
dev->msg_write_idx > 0)
need_restart = true;
}
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 42b34c678146..069c3dc0da10 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -257,6 +257,7 @@ static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "mobileye,eyeq6lplus-i2c" },
{ .compatible = "mscc,ocelot-i2c" },
{ .compatible = "snps,designware-i2c" },
+ { .compatible = "ti,tda54-i2c", .data = (void *)MODEL_TI_TDA54 },
{}
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support
2026-09-25 6:56 [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Aniket Limaye
` (2 preceding siblings ...)
2026-09-25 6:56 ` [PATCH v3 3/3] i2c: designware: Add ti,tda54-i2c support Aniket Limaye
@ 2026-09-25 9:41 ` Andy Shevchenko
[not found] ` <e0f113ea-92cc-4e82-85ae-f61814c230c7@ti.com>
3 siblings, 1 reply; 8+ messages in thread
From: Andy Shevchenko @ 2026-09-25 9:41 UTC (permalink / raw)
To: Aniket Limaye
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel
On Fri, Sep 25, 2026 at 12:26:27PM +0530, Aniket Limaye wrote:
> Add new compatible and update driver to support Synopsys Advanced I2C
> Controller (DWC_i2c [0]) present on TI TDA54 SoC. This is needed since
> this controller differs from the existing designware i2c (DW_apb_i2c
> [1]) in its register offsets and some register definitions.
>
> The new compatible updates driver logic due to changes in register
> definitions:
>
> - The new register offsets are handled by first refactoring the driver
> to use a map of register IDs to their offsets.
> - Similarly, also update the driver to use an updated CON-register
> bitfield definition.
> - Interrupts are acknowledged by writing a bitmask to a single CLR_INTR
> register instead of reading N dedicated CLR_* registers;
> i2c_dw_ack_intr() picks the right method based on dev->flags.
> - One HCNT/LCNT register pair is shared between standard and fast speed
> instead of having one pair each; i2c_dw_write_timings() writes
> whichever value set matches the configured speed.
> - No COMP_PARAM_1 register, so FIFO depth and high-speed-mode support
> can't be autodetected: FIFO depth now comes from the required
> snps,tx-fifo-depth/snps,rx-fifo-depth DT properties, and the
> high-speed capability check is skipped.
> - No defined CON.RESTART_EN bit; treat it as always set.
> - TDA54 SoC does not support SMBus, so skip accesses to those registers.
>
> [0]: DWC_i2c_reference.pdf
> [1]: DW_apb_i2c_databook.pdf
Still doesn't look good. The current register layout may be left as is. What
you need is translate it in the respective regmap callbacks in case we are
enumerated on the different IP. Also possible to have a different regmap
config for the different HW where you translate them only in one place.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support
[not found] ` <e0f113ea-92cc-4e82-85ae-f61814c230c7@ti.com>
@ 2026-09-25 10:10 ` Andy Shevchenko
2026-09-25 11:16 ` Aniket Limaye
0 siblings, 1 reply; 8+ messages in thread
From: Andy Shevchenko @ 2026-09-25 10:10 UTC (permalink / raw)
To: Aniket Limaye
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel
On Fri, Sep 25, 2026 at 03:29:27PM +0530, Aniket Limaye wrote:
> On 25/09/26 15:11, Andy Shevchenko wrote:
> > On Fri, Sep 25, 2026 at 12:26:27PM +0530, Aniket Limaye wrote:
...
> > Still doesn't look good. The current register layout may be left as is. What
> > you need is translate it in the respective regmap callbacks in case we are
> > enumerated on the different IP. Also possible to have a different regmap
> > config for the different HW where you translate them only in one place.
>
> Is it confusing to keep using existing offsets in regmap_read/write() call
> sites for TDA54, and let the regmap silently handle the translation?
>
> Given that we do *not* have any new registers in use on the tda54 version
> that were not there in the original one, I guess it works... will send a v4
> as per your suggestion.
Depends on the mapping. Your series also forgot to provide the differences
Check this as an example: e539f435cb9c ("spi: dw: Add support for DesignWare DWC_ssi").
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support
2026-09-25 10:10 ` Andy Shevchenko
@ 2026-09-25 11:16 ` Aniket Limaye
2026-09-25 12:53 ` Andy Shevchenko
0 siblings, 1 reply; 8+ messages in thread
From: Aniket Limaye @ 2026-09-25 11:16 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel
On 25/09/26 15:40, Andy Shevchenko wrote:
> On Fri, Sep 25, 2026 at 03:29:27PM +0530, Aniket Limaye wrote:
>> On 25/09/26 15:11, Andy Shevchenko wrote:
>>> On Fri, Sep 25, 2026 at 12:26:27PM +0530, Aniket Limaye wrote:
> ...
>
>>> Still doesn't look good. The current register layout may be left as is. What
>>> you need is translate it in the respective regmap callbacks in case we are
>>> enumerated on the different IP. Also possible to have a different regmap
>>> config for the different HW where you translate them only in one place.
>> Is it confusing to keep using existing offsets in regmap_read/write() call
>> sites for TDA54, and let the regmap silently handle the translation?
>>
>> Given that we do *not* have any new registers in use on the tda54 version
>> that were not there in the original one, I guess it works... will send a v4
>> as per your suggestion.
> Depends on the mapping. Your series also forgot to provide the differences
> Check this as an example: e539f435cb9c ("spi: dw: Add support for DesignWare DWC_ssi").
>
Ahh sorry about that, will remember to add a clean mapping in cover
letter of next version.
For now here are the structural differences:
1. Reg offsets Existing New TDA54
[DW_IC_CON] 0x00 0x2c
[DW_IC_TAR] 0x04 0x30
[DW_IC_SAR] 0x08 0x34
[DW_IC_DATA_CMD] 0x10 0x80
[DW_IC_SS_SCL_HCNT] 0x14 0x4c
[DW_IC_SS_SCL_LCNT] 0x18 0x50
[DW_IC_FS_SCL_HCNT] 0x1c 0x4c /* same as SS */
[DW_IC_FS_SCL_LCNT] 0x20 0x50 /* same as SS */
[DW_IC_HS_SCL_HCNT] 0x24 0x54
[DW_IC_HS_SCL_LCNT] 0x28 0x58
[DW_IC_INTR_STAT] 0x2c 0xc0
[DW_IC_INTR_MASK] 0x30 0xc4
[DW_IC_RAW_INTR_STAT] 0x34 0xc8
[DW_IC_RX_TL] 0x38 0x84
[DW_IC_TX_TL] 0x3c 0x88
[DW_IC_CLR_INTR] 0x40 0xcc
[DW_IC_CLR_RX_UNDER] 0x44 NA
[DW_IC_CLR_RX_OVER] 0x48 NA
[DW_IC_CLR_TX_OVER] 0x4c NA
[DW_IC_CLR_RD_REQ] 0x50 NA
[DW_IC_CLR_TX_ABRT] 0x54 NA
[DW_IC_CLR_RX_DONE] 0x58 NA
[DW_IC_CLR_ACTIVITY] 0x5c NA
[DW_IC_CLR_STOP_DET] 0x60 NA
[DW_IC_CLR_START_DET] 0x64 NA
[DW_IC_CLR_GEN_CALL] 0x68 NA
[DW_IC_ENABLE] 0x6c 0x04
[DW_IC_STATUS] 0x70 0xd8
[DW_IC_TXFLR] 0x74 0xdc
[DW_IC_RXFLR] 0x78 0xe0
[DW_IC_SDA_HOLD] 0x7c 0x5c
[DW_IC_TX_ABRT_SOURCE] 0x80 0xd4
[DW_IC_ENABLE_STATUS] 0x9c 0xd0
[DW_IC_CLR_RESTART_DET] 0xa8 NA
[DW_IC_SMBUS_INTR_STAT] 0xc8 NA
[DW_IC_SMBUS_INTR_MASK] 0xcc NA
[DW_IC_CLR_SMBUS_INTR] 0xd4 NA
[DW_IC_COMP_PARAM_1] 0xf4 NA
[DW_IC_COMP_VERSION] 0xf8 0x100
[DW_IC_COMP_TYPE] 0xfc 0x104
2. DW_IC_CON bitfields:
DW_IC_CON_MASTER BIT(0) BIT(0)
DW_IC_CON_SPEED_STD (1 << 1) (1 << 4)
DW_IC_CON_SPEED_FAST (2 << 1) (2 << 4)
DW_IC_CON_SPEED_HIGH (3 << 1) (3 << 4)
DW_IC_CON_SPEED_MASK GENMASK(2, 1) GENMASK(5, 4)
DW_IC_CON_10BITADDR_SLAVE BIT(3) BIT(8)
DW_IC_CON_10BITADDR_MASTER BIT(4) BIT(9)
DW_IC_CON_RESTART_EN BIT(5) NA
DW_IC_CON_SLAVE_DISABLE BIT(6) NA
DW_IC_CON_STOP_DET_IFADDRESSED BIT(7) BIT(10)
DW_IC_CON_TX_EMPTY_CTRL BIT(8) BIT(11)
DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) BIT(12)
DW_IC_CON_BUS_CLEAR_CTRL BIT(11) BIT(14)
3. To clear INTR, Read DW_IC_CLR_* reg Write bit to DW_IC_CLR_INTR
As you can see, it's an entirely different mapping, which is why I had 2
independent enum -> reg offset maps instead of offset -> offset
translation.
Similarly, selecting a CON register bitfield layout too.
Let me know what you would prefer based on this...
Thanks,
Aniket
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support
2026-09-25 11:16 ` Aniket Limaye
@ 2026-09-25 12:53 ` Andy Shevchenko
0 siblings, 0 replies; 8+ messages in thread
From: Andy Shevchenko @ 2026-09-25 12:53 UTC (permalink / raw)
To: Aniket Limaye
Cc: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Mika Westerberg, Nirujogi Pratap, Bin Du, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
linux-i2c, devicetree, linux-kernel, vigneshr, nm, u-kumar1,
lianfeng.ouyang, Ritwick.Sharma, intel-xe, dri-devel
On Fri, Sep 25, 2026 at 04:46:39PM +0530, Aniket Limaye wrote:
> On 25/09/26 15:40, Andy Shevchenko wrote:
> > On Fri, Sep 25, 2026 at 03:29:27PM +0530, Aniket Limaye wrote:
> > > On 25/09/26 15:11, Andy Shevchenko wrote:
> > > > On Fri, Sep 25, 2026 at 12:26:27PM +0530, Aniket Limaye wrote:
...
> > > > Still doesn't look good. The current register layout may be left as is. What
> > > > you need is translate it in the respective regmap callbacks in case we are
> > > > enumerated on the different IP. Also possible to have a different regmap
> > > > config for the different HW where you translate them only in one place.
> > > Is it confusing to keep using existing offsets in regmap_read/write() call
> > > sites for TDA54, and let the regmap silently handle the translation?
> > >
> > > Given that we do *not* have any new registers in use on the tda54 version
> > > that were not there in the original one, I guess it works... will send a v4
> > > as per your suggestion.
> > Depends on the mapping. Your series also forgot to provide the differences
> > Check this as an example: e539f435cb9c ("spi: dw: Add support for DesignWare DWC_ssi").
> >
> Ahh sorry about that, will remember to add a clean mapping in cover letter
> of next version.
>
> For now here are the structural differences:
>
> 1. Reg offsets Existing New TDA54
>
> [DW_IC_CON] 0x00 0x2c
> [DW_IC_TAR] 0x04 0x30
> [DW_IC_SAR] 0x08 0x34
> [DW_IC_DATA_CMD] 0x10 0x80
> [DW_IC_SS_SCL_HCNT] 0x14 0x4c
> [DW_IC_SS_SCL_LCNT] 0x18 0x50
> [DW_IC_FS_SCL_HCNT] 0x1c 0x4c /* same as SS */
> [DW_IC_FS_SCL_LCNT] 0x20 0x50 /* same as SS */
> [DW_IC_HS_SCL_HCNT] 0x24 0x54
> [DW_IC_HS_SCL_LCNT] 0x28 0x58
> [DW_IC_INTR_STAT] 0x2c 0xc0
> [DW_IC_INTR_MASK] 0x30 0xc4
> [DW_IC_RAW_INTR_STAT] 0x34 0xc8
> [DW_IC_RX_TL] 0x38 0x84
> [DW_IC_TX_TL] 0x3c 0x88
> [DW_IC_CLR_INTR] 0x40 0xcc
> [DW_IC_CLR_RX_UNDER] 0x44 NA
> [DW_IC_CLR_RX_OVER] 0x48 NA
> [DW_IC_CLR_TX_OVER] 0x4c NA
> [DW_IC_CLR_RD_REQ] 0x50 NA
> [DW_IC_CLR_TX_ABRT] 0x54 NA
> [DW_IC_CLR_RX_DONE] 0x58 NA
> [DW_IC_CLR_ACTIVITY] 0x5c NA
> [DW_IC_CLR_STOP_DET] 0x60 NA
> [DW_IC_CLR_START_DET] 0x64 NA
> [DW_IC_CLR_GEN_CALL] 0x68 NA
> [DW_IC_ENABLE] 0x6c 0x04
> [DW_IC_STATUS] 0x70 0xd8
> [DW_IC_TXFLR] 0x74 0xdc
> [DW_IC_RXFLR] 0x78 0xe0
> [DW_IC_SDA_HOLD] 0x7c 0x5c
> [DW_IC_TX_ABRT_SOURCE] 0x80 0xd4
> [DW_IC_ENABLE_STATUS] 0x9c 0xd0
> [DW_IC_CLR_RESTART_DET] 0xa8 NA
> [DW_IC_SMBUS_INTR_STAT] 0xc8 NA
> [DW_IC_SMBUS_INTR_MASK] 0xcc NA
> [DW_IC_CLR_SMBUS_INTR] 0xd4 NA
> [DW_IC_COMP_PARAM_1] 0xf4 NA
> [DW_IC_COMP_VERSION] 0xf8 0x100
> [DW_IC_COMP_TYPE] 0xfc 0x104
>
> 2. DW_IC_CON bitfields:
>
> DW_IC_CON_MASTER BIT(0) BIT(0)
> DW_IC_CON_SPEED_STD (1 << 1) (1 << 4)
> DW_IC_CON_SPEED_FAST (2 << 1) (2 << 4)
> DW_IC_CON_SPEED_HIGH (3 << 1) (3 << 4)
> DW_IC_CON_SPEED_MASK GENMASK(2, 1) GENMASK(5, 4)
> DW_IC_CON_10BITADDR_SLAVE BIT(3) BIT(8)
> DW_IC_CON_10BITADDR_MASTER BIT(4) BIT(9)
> DW_IC_CON_RESTART_EN BIT(5) NA
> DW_IC_CON_SLAVE_DISABLE BIT(6) NA
> DW_IC_CON_STOP_DET_IFADDRESSED BIT(7) BIT(10)
> DW_IC_CON_TX_EMPTY_CTRL BIT(8) BIT(11)
> DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) BIT(12)
> DW_IC_CON_BUS_CLEAR_CTRL BIT(11) BIT(14)
Thanks for providing this mapping!
> 3. To clear INTR, Read DW_IC_CLR_* reg Write bit to DW_IC_CLR_INTR
>
> As you can see, it's an entirely different mapping, which is why I had 2
> independent enum -> reg offset maps instead of offset -> offset translation.
> Similarly, selecting a CON register bitfield layout too.
>
> Let me know what you would prefer based on this...
This clears a bit the whole picture and what I would like to say is that better
to have the separate driver for it. On top of completely reworked RTL I believe
you won't need tons of hacks and workarounds that are applied during all these
years against the old IP.
TL;DR: Make a new clean and simple driver, which is hack-less and done properly
(using the all modern APIs and frameworks in the kernel).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-25 12:53 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 6:56 [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 1/3] dt-bindings: i2c: dw: Add ti,tda54-i2c compatible Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 2/3] i2c: designware: Support per-variant reg/bit offsets and intr handling Aniket Limaye
2026-09-25 6:56 ` [PATCH v3 3/3] i2c: designware: Add ti,tda54-i2c support Aniket Limaye
2026-09-25 9:41 ` [PATCH v3 0/3] i2c: designware: Add TI TDA54 I2C support Andy Shevchenko
[not found] ` <e0f113ea-92cc-4e82-85ae-f61814c230c7@ti.com>
2026-09-25 10:10 ` Andy Shevchenko
2026-09-25 11:16 ` Aniket Limaye
2026-09-25 12:53 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®