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 <[email protected]>

---

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/[email protected]/
---
 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

Reply via email to