i2c: designware: Add SMBUS quick command support
authorPhil Elwell <phil@raspberrypi.com>
Fri, 4 Dec 2020 15:20:36 +0000 (15:20 +0000)
committerDom Cobley <popcornmix@gmail.com>
Mon, 19 Feb 2024 11:34:47 +0000 (11:34 +0000)
The SMBUS emulation code turns an SMBUS quick command into a zero-
length read. This controller can't do zero length accesses, but it
can do quick commands, so reverse the emulation. The alternative
would be to properly implement the SMBUS support but that is a lot
more work, and unnecessary just to get i2cdetect working.

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-master.c

index a7f6f3e..2762c3e 100644 (file)
 
 #define DW_IC_ERR_TX_ABRT                      0x1
 
+#define DW_IC_TAR_SPECIAL                      BIT(11)
 #define DW_IC_TAR_10BITADDR_MASTER             BIT(12)
+#define DW_IC_TAR_SMBUS_QUICK_CMD              BIT(16)
 
 #define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH     (BIT(2) | BIT(3))
 #define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK     GENMASK(3, 2)
index 85dbd0e..ee04634 100644 (file)
@@ -229,6 +229,10 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
                ic_tar = DW_IC_TAR_10BITADDR_MASTER;
        }
 
+       /* Convert a zero-length read into an SMBUS quick command */
+       if (!msgs[dev->msg_write_idx].len)
+               ic_tar = DW_IC_TAR_SPECIAL | DW_IC_TAR_SMBUS_QUICK_CMD;
+
        regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
                           ic_con);
 
@@ -472,6 +476,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
                regmap_read(dev->map, DW_IC_RXFLR, &flr);
                rx_limit = dev->rx_fifo_depth - flr;
 
+               /* Handle SMBUS quick commands */
+               if (!buf_len) {
+                       if (msgs[dev->msg_write_idx].flags & I2C_M_RD)
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x300);
+                       else
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x200);
+               }
+
                while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
                        u32 cmd = 0;
 
@@ -743,7 +755,7 @@ static const struct i2c_algorithm i2c_dw_algo = {
 };
 
 static const struct i2c_adapter_quirks i2c_dw_quirks = {
-       .flags = I2C_AQ_NO_ZERO_LEN,
+       .flags = 0,
 };
 
 static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
@@ -876,7 +888,8 @@ void i2c_dw_configure_master(struct dw_i2c_dev *dev)
 {
        struct i2c_timings *t = &dev->timings;
 
-       dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
+       dev->functionality = I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_QUICK |
+                            DW_IC_DEFAULT_FUNCTIONALITY;
 
        dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
                          DW_IC_CON_RESTART_EN;