Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i2c: designware: Enable mode swapping

The DesignWare I2C can not be operated as I2C master and
I2C slave simultaneously, but that does not actually mean
master and slave modes can not be supported at the same
time. It just means an explicit mode swap needs to be
executed when the mode is changed. The DesignWare I2C
documentation actually describes a couple of cases where the
mode is excepted to be changed.

The I2C master will now always be supported. Both modes are
now always configured in i2c_dw_configure(), but the slave
mode will continue to be available only when the Kconfig
option I2C_SLAVE is enabled.

The driver will now start in master mode and then swap to
slave mode when a slave device is registered. After a slave
device is registered, the controller is swapped to master
mode when a transfer in master mode is started and then back
to slave mode again after the transfer is completed.

The DesignWare I2C can now be used with protocols such as
MCTP (drivers/net/mctp/mctp-i2c.c) and IPMI
(drivers/char/ipmi/) that require support for both I2C
master and I2C slave. It is now also possible to support the
SMBus Host Notification Protocol as I2C master if needed.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260120130729.1679560-4-heikki.krogerus@linux.intel.com

authored by

Heikki Krogerus and committed by
Andi Shyti
cfbcc20d 38fa29b0

+57 -43
+34 -16
drivers/i2c/busses/i2c-designware-common.c
··· 359 359 360 360 #endif /* CONFIG_ACPI */ 361 361 362 - static void i2c_dw_configure_mode(struct dw_i2c_dev *dev) 362 + static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode) 363 363 { 364 - switch (dev->mode) { 364 + switch (mode) { 365 365 case DW_IC_MASTER: 366 366 regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2); 367 367 regmap_write(dev->map, DW_IC_RX_TL, 0); 368 368 regmap_write(dev->map, DW_IC_CON, dev->master_cfg); 369 369 break; 370 370 case DW_IC_SLAVE: 371 + dev->status = 0; 371 372 regmap_write(dev->map, DW_IC_TX_TL, 0); 372 373 regmap_write(dev->map, DW_IC_RX_TL, 0); 373 374 regmap_write(dev->map, DW_IC_CON, dev->slave_cfg); 375 + regmap_write(dev->map, DW_IC_SAR, dev->slave->addr); 374 376 regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK); 377 + __i2c_dw_enable(dev); 375 378 break; 376 379 default: 380 + WARN(1, "Invalid mode %d\n", mode); 377 381 return; 378 382 } 379 383 } ··· 397 393 regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt); 398 394 regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); 399 395 } 396 + } 397 + 398 + /** 399 + * i2c_dw_set_mode() - Select the controller mode of operation - master or slave 400 + * @dev: device private data 401 + * @mode: I2C mode of operation 402 + * 403 + * Configures the controller to operate in @mode. This function needs to be 404 + * called when ever a mode swap is required. 405 + * 406 + * Setting the slave mode does not have an effect before a slave device is 407 + * registered. So before the slave device is registered, the controller is kept 408 + * in master mode regardless of @mode. 409 + * 410 + * The controller must be disabled before this function is called. 411 + */ 412 + void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode) 413 + { 414 + if (mode == DW_IC_SLAVE && !dev->slave) 415 + mode = DW_IC_MASTER; 416 + if (dev->mode == mode) 417 + return; 418 + 419 + i2c_dw_configure_mode(dev, mode); 420 + dev->mode = mode; 400 421 } 401 422 402 423 /** ··· 450 421 */ 451 422 regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0); 452 423 453 - if (dev->mode == DW_IC_MASTER) 454 - i2c_dw_write_timings(dev); 424 + i2c_dw_write_timings(dev); 455 425 456 426 /* Write SDA hold time if supported */ 457 427 if (dev->sda_hold_time) 458 428 regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); 459 429 460 - i2c_dw_configure_mode(dev); 430 + i2c_dw_configure_mode(dev, dev->mode); 461 431 462 432 i2c_dw_release_lock(dev); 463 433 ··· 892 864 if (ret) 893 865 return ret; 894 866 895 - switch (dev->mode) { 896 - case DW_IC_SLAVE: 897 - ret = i2c_dw_probe_slave(dev); 898 - break; 899 - case DW_IC_MASTER: 900 - ret = i2c_dw_probe_master(dev); 901 - break; 902 - default: 903 - ret = -EINVAL; 904 - break; 905 - } 867 + ret = i2c_dw_probe_master(dev); 906 868 if (ret) 907 869 return ret; 908 870
+3 -6
drivers/i2c/busses/i2c-designware-core.h
··· 398 398 399 399 #if IS_ENABLED(CONFIG_I2C_SLAVE) 400 400 extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev); 401 - extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev); 402 401 irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev); 403 402 int i2c_dw_reg_slave(struct i2c_client *client); 404 403 int i2c_dw_unreg_slave(struct i2c_client *client); 405 404 #else 406 405 static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { } 407 - static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; } 408 406 static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; } 409 407 #endif 410 408 411 409 static inline void i2c_dw_configure(struct dw_i2c_dev *dev) 412 410 { 413 - if (i2c_detect_slave_mode(dev->dev)) 414 - i2c_dw_configure_slave(dev); 415 - else 416 - i2c_dw_configure_master(dev); 411 + i2c_dw_configure_slave(dev); 412 + i2c_dw_configure_master(dev); 417 413 } 418 414 419 415 int i2c_dw_probe(struct dw_i2c_dev *dev); 420 416 int i2c_dw_init(struct dw_i2c_dev *dev); 417 + void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode); 421 418 422 419 #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) 423 420 int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
+5 -1
drivers/i2c/busses/i2c-designware-master.c
··· 194 194 /* Disable the adapter */ 195 195 __i2c_dw_disable(dev); 196 196 197 + i2c_dw_set_mode(dev, DW_IC_MASTER); 198 + 197 199 /* If the slave address is ten bit address, enable 10BITADDR */ 198 200 if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { 199 201 ic_con = DW_IC_CON_10BITADDR_MASTER; ··· 833 831 ret = -EIO; 834 832 835 833 done: 834 + i2c_dw_set_mode(dev, DW_IC_SLAVE); 835 + 836 836 i2c_dw_release_lock(dev); 837 837 838 838 done_nolock: ··· 857 853 { 858 854 struct i2c_timings *t = &dev->timings; 859 855 860 - dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; 856 + dev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; 861 857 862 858 dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | 863 859 DW_IC_CON_RESTART_EN;
+15 -20
drivers/i2c/busses/i2c-designware-slave.c
··· 24 24 int i2c_dw_reg_slave(struct i2c_client *slave) 25 25 { 26 26 struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); 27 + int ret; 27 28 29 + if (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE)) 30 + return -EOPNOTSUPP; 28 31 if (dev->slave) 29 32 return -EBUSY; 30 33 if (slave->flags & I2C_CLIENT_TEN) 31 34 return -EAFNOSUPPORT; 35 + 36 + ret = i2c_dw_acquire_lock(dev); 37 + if (ret) 38 + return ret; 39 + 32 40 pm_runtime_get_sync(dev->dev); 33 - 34 - /* 35 - * Set slave address in the IC_SAR register, 36 - * the address to which the DW_apb_i2c responds. 37 - */ 38 41 __i2c_dw_disable_nowait(dev); 39 - regmap_write(dev->map, DW_IC_SAR, slave->addr); 40 42 dev->slave = slave; 43 + i2c_dw_set_mode(dev, DW_IC_SLAVE); 41 44 42 - __i2c_dw_enable(dev); 43 - 44 - dev->status = 0; 45 + i2c_dw_release_lock(dev); 45 46 46 47 return 0; 47 48 } ··· 55 54 i2c_dw_disable(dev); 56 55 synchronize_irq(dev->irq); 57 56 dev->slave = NULL; 57 + i2c_dw_set_mode(dev, DW_IC_MASTER); 58 58 pm_runtime_put_sync_suspend(dev->dev); 59 59 60 60 return 0; ··· 178 176 179 177 void i2c_dw_configure_slave(struct dw_i2c_dev *dev) 180 178 { 181 - dev->functionality = I2C_FUNC_SLAVE; 179 + if (dev->flags & ACCESS_POLLING) 180 + return; 181 + 182 + dev->functionality |= I2C_FUNC_SLAVE; 182 183 183 184 dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL | 184 185 DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED; 185 - 186 - dev->mode = DW_IC_SLAVE; 187 186 } 188 187 EXPORT_SYMBOL_GPL(i2c_dw_configure_slave); 189 - 190 - int i2c_dw_probe_slave(struct dw_i2c_dev *dev) 191 - { 192 - if (dev->flags & ACCESS_POLLING) 193 - return -EOPNOTSUPP; 194 - 195 - return 0; 196 - } 197 188 198 189 MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>"); 199 190 MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");