diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 2bf4f60a9ac22337dc201327561ae7596399c7ae..e1f1b75197714401b7600ac0b2282b367569da42 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -460,27 +460,29 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; - u32 ic_con, ic_tar = 0; + u32 ic_tar = 0; /* Disable the adapter */ __i2c_dw_enable_and_wait(dev, false); /* if the slave address is ten bit address, enable 10BITADDR */ - ic_con = dw_readl(dev, DW_IC_CON); - if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { - ic_con |= DW_IC_CON_10BITADDR_MASTER; + if (dev->dynamic_tar_update_enabled) { /* * If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing - * mode has to be enabled via bit 12 of IC_TAR register. - * We set it always as I2C_DYNAMIC_TAR_UPDATE can't be - * detected from registers. + * mode has to be enabled via bit 12 of IC_TAR register, + * otherwise bit 4 of IC_CON is used. */ - ic_tar = DW_IC_TAR_10BITADDR_MASTER; + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_tar = DW_IC_TAR_10BITADDR_MASTER; } else { - ic_con &= ~DW_IC_CON_10BITADDR_MASTER; - } + u32 ic_con = dw_readl(dev, DW_IC_CON); - dw_writel(dev, ic_con, DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + dw_writel(dev, ic_con, DW_IC_CON); + } /* * Set the slave (target) address and enable 10-bit addressing mode @@ -901,6 +903,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; int r; + u32 reg; init_completion(&dev->cmd_complete); @@ -908,6 +911,26 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) if (r) return r; + r = i2c_dw_acquire_lock(dev); + if (r) + return r; + + /* + * Test if dynamic TAR update is enabled in this controller by writing + * to IC_10BITADDR_MASTER field in IC_CON: when it is enabled this + * field is read-only so it should not succeed + */ + reg = dw_readl(dev, DW_IC_CON); + dw_writel(dev, reg ^ DW_IC_CON_10BITADDR_MASTER, DW_IC_CON); + + if ((dw_readl(dev, DW_IC_CON) & DW_IC_CON_10BITADDR_MASTER) == + (reg & DW_IC_CON_10BITADDR_MASTER)) { + dev->dynamic_tar_update_enabled = true; + dev_dbg(dev->dev, "Dynamic TAR update enabled"); + } + + i2c_dw_release_lock(dev); + snprintf(adap->name, sizeof(adap->name), "Synopsys DesignWare I2C adapter"); adap->retries = 3; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 22bfbe14787093f0da4bc45060fdae1822a7848a..0d44d2ae7d4c2ea9d091f446bccc76efa98f926d 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -117,6 +117,7 @@ struct dw_i2c_dev { int (*acquire_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev); bool pm_runtime_disabled; + bool dynamic_tar_update_enabled; }; #define ACCESS_SWAP 0x00000001