diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 17affdecbe30..53d9337db5e2 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -359,21 +359,25 @@ static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0; #endif /* CONFIG_ACPI */ -static void i2c_dw_configure_mode(struct dw_i2c_dev *dev) +static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode) { - switch (dev->mode) { + switch (mode) { case DW_IC_MASTER: regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2); regmap_write(dev->map, DW_IC_RX_TL, 0); regmap_write(dev->map, DW_IC_CON, dev->master_cfg); break; case DW_IC_SLAVE: + dev->status = 0; regmap_write(dev->map, DW_IC_TX_TL, 0); regmap_write(dev->map, DW_IC_RX_TL, 0); regmap_write(dev->map, DW_IC_CON, dev->slave_cfg); + regmap_write(dev->map, DW_IC_SAR, dev->slave->addr); regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK); + __i2c_dw_enable(dev); break; default: + WARN(1, "Invalid mode %d\n", mode); return; } } @@ -395,6 +399,31 @@ static void i2c_dw_write_timings(struct dw_i2c_dev *dev) } } +/** + * i2c_dw_set_mode() - Select the controller mode of operation - master or slave + * @dev: device private data + * @mode: I2C mode of operation + * + * Configures the controller to operate in @mode. This function needs to be + * called when ever a mode swap is required. + * + * Setting the slave mode does not have an effect before a slave device is + * registered. So before the slave device is registered, the controller is kept + * in master mode regardless of @mode. + * + * The controller must be disabled before this function is called. + */ +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode) +{ + if (mode == DW_IC_SLAVE && !dev->slave) + mode = DW_IC_MASTER; + if (dev->mode == mode) + return; + + i2c_dw_configure_mode(dev, mode); + dev->mode = mode; +} + /** * i2c_dw_init() - Initialize the DesignWare I2C hardware * @dev: device private data @@ -421,14 +450,13 @@ int i2c_dw_init(struct dw_i2c_dev *dev) */ regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0); - if (dev->mode == DW_IC_MASTER) - i2c_dw_write_timings(dev); + i2c_dw_write_timings(dev); /* Write SDA hold time if supported */ if (dev->sda_hold_time) regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); - i2c_dw_configure_mode(dev); + i2c_dw_configure_mode(dev, dev->mode); i2c_dw_release_lock(dev); @@ -864,17 +892,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) if (ret) return ret; - switch (dev->mode) { - case DW_IC_SLAVE: - ret = i2c_dw_probe_slave(dev); - break; - case DW_IC_MASTER: - ret = i2c_dw_probe_master(dev); - break; - default: - ret = -EINVAL; - break; - } + ret = i2c_dw_probe_master(dev); if (ret) return ret; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 63a59bb03163..a49263a36023 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -398,26 +398,23 @@ int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); #if IS_ENABLED(CONFIG_I2C_SLAVE) extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev); -extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev); irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev); int i2c_dw_reg_slave(struct i2c_client *client); int i2c_dw_unreg_slave(struct i2c_client *client); #else static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { } -static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; } static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; } #endif static inline void i2c_dw_configure(struct dw_i2c_dev *dev) { - if (i2c_detect_slave_mode(dev->dev)) - i2c_dw_configure_slave(dev); - else - i2c_dw_configure_master(dev); + i2c_dw_configure_slave(dev); + i2c_dw_configure_master(dev); } int i2c_dw_probe(struct dw_i2c_dev *dev); int i2c_dw_init(struct dw_i2c_dev *dev); +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode); #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 22c1bb463c2c..8ca254cbb2f8 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -194,6 +194,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) /* Disable the adapter */ __i2c_dw_disable(dev); + i2c_dw_set_mode(dev, DW_IC_MASTER); + /* 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; @@ -831,6 +833,8 @@ i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num) ret = -EIO; done: + i2c_dw_set_mode(dev, DW_IC_SLAVE); + i2c_dw_release_lock(dev); done_nolock: @@ -853,7 +857,7 @@ 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 | DW_IC_DEFAULT_FUNCTIONALITY; dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN; diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index 9fc8faa33735..ad0d5fbfa6d5 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -24,24 +24,25 @@ int i2c_dw_reg_slave(struct i2c_client *slave) { struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + int ret; + if (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE)) + return -EOPNOTSUPP; if (dev->slave) return -EBUSY; if (slave->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + pm_runtime_get_sync(dev->dev); - - /* - * Set slave address in the IC_SAR register, - * the address to which the DW_apb_i2c responds. - */ __i2c_dw_disable_nowait(dev); - regmap_write(dev->map, DW_IC_SAR, slave->addr); dev->slave = slave; + i2c_dw_set_mode(dev, DW_IC_SLAVE); - __i2c_dw_enable(dev); - - dev->status = 0; + i2c_dw_release_lock(dev); return 0; } @@ -54,6 +55,7 @@ int i2c_dw_unreg_slave(struct i2c_client *slave) i2c_dw_disable(dev); synchronize_irq(dev->irq); dev->slave = NULL; + i2c_dw_set_mode(dev, DW_IC_MASTER); pm_runtime_put_sync_suspend(dev->dev); return 0; @@ -176,23 +178,16 @@ irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { - dev->functionality = I2C_FUNC_SLAVE; + if (dev->flags & ACCESS_POLLING) + return; + + 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->mode = DW_IC_SLAVE; } EXPORT_SYMBOL_GPL(i2c_dw_configure_slave); -int i2c_dw_probe_slave(struct dw_i2c_dev *dev) -{ - if (dev->flags & ACCESS_POLLING) - return -EOPNOTSUPP; - - return 0; -} - MODULE_AUTHOR("Luis Oliveira "); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter"); MODULE_LICENSE("GPL v2");