Skip to content

Commit cfbcc20

Browse files
Heikki KrogerusAndi Shyti
authored andcommitted
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
1 parent 38fa29b commit cfbcc20

4 files changed

Lines changed: 57 additions & 43 deletions

File tree

drivers/i2c/busses/i2c-designware-common.c

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -359,21 +359,25 @@ static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0;
359359

360360
#endif /* CONFIG_ACPI */
361361

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)
363363
{
364-
switch (dev->mode) {
364+
switch (mode) {
365365
case DW_IC_MASTER:
366366
regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);
367367
regmap_write(dev->map, DW_IC_RX_TL, 0);
368368
regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
369369
break;
370370
case DW_IC_SLAVE:
371+
dev->status = 0;
371372
regmap_write(dev->map, DW_IC_TX_TL, 0);
372373
regmap_write(dev->map, DW_IC_RX_TL, 0);
373374
regmap_write(dev->map, DW_IC_CON, dev->slave_cfg);
375+
regmap_write(dev->map, DW_IC_SAR, dev->slave->addr);
374376
regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK);
377+
__i2c_dw_enable(dev);
375378
break;
376379
default:
380+
WARN(1, "Invalid mode %d\n", mode);
377381
return;
378382
}
379383
}
@@ -395,6 +399,31 @@ static void i2c_dw_write_timings(struct dw_i2c_dev *dev)
395399
}
396400
}
397401

402+
/**
403+
* i2c_dw_set_mode() - Select the controller mode of operation - master or slave
404+
* @dev: device private data
405+
* @mode: I2C mode of operation
406+
*
407+
* Configures the controller to operate in @mode. This function needs to be
408+
* called when ever a mode swap is required.
409+
*
410+
* Setting the slave mode does not have an effect before a slave device is
411+
* registered. So before the slave device is registered, the controller is kept
412+
* in master mode regardless of @mode.
413+
*
414+
* The controller must be disabled before this function is called.
415+
*/
416+
void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)
417+
{
418+
if (mode == DW_IC_SLAVE && !dev->slave)
419+
mode = DW_IC_MASTER;
420+
if (dev->mode == mode)
421+
return;
422+
423+
i2c_dw_configure_mode(dev, mode);
424+
dev->mode = mode;
425+
}
426+
398427
/**
399428
* i2c_dw_init() - Initialize the DesignWare I2C hardware
400429
* @dev: device private data
@@ -421,14 +450,13 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
421450
*/
422451
regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
423452

424-
if (dev->mode == DW_IC_MASTER)
425-
i2c_dw_write_timings(dev);
453+
i2c_dw_write_timings(dev);
426454

427455
/* Write SDA hold time if supported */
428456
if (dev->sda_hold_time)
429457
regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
430458

431-
i2c_dw_configure_mode(dev);
459+
i2c_dw_configure_mode(dev, dev->mode);
432460

433461
i2c_dw_release_lock(dev);
434462

@@ -864,17 +892,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
864892
if (ret)
865893
return ret;
866894

867-
switch (dev->mode) {
868-
case DW_IC_SLAVE:
869-
ret = i2c_dw_probe_slave(dev);
870-
break;
871-
case DW_IC_MASTER:
872-
ret = i2c_dw_probe_master(dev);
873-
break;
874-
default:
875-
ret = -EINVAL;
876-
break;
877-
}
895+
ret = i2c_dw_probe_master(dev);
878896
if (ret)
879897
return ret;
880898

drivers/i2c/busses/i2c-designware-core.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -398,26 +398,23 @@ int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
398398

399399
#if IS_ENABLED(CONFIG_I2C_SLAVE)
400400
extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev);
401-
extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev);
402401
irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev);
403402
int i2c_dw_reg_slave(struct i2c_client *client);
404403
int i2c_dw_unreg_slave(struct i2c_client *client);
405404
#else
406405
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; }
408406
static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; }
409407
#endif
410408

411409
static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
412410
{
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);
417413
}
418414

419415
int i2c_dw_probe(struct dw_i2c_dev *dev);
420416
int i2c_dw_init(struct dw_i2c_dev *dev);
417+
void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode);
421418

422419
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
423420
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);

drivers/i2c/busses/i2c-designware-master.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
194194
/* Disable the adapter */
195195
__i2c_dw_disable(dev);
196196

197+
i2c_dw_set_mode(dev, DW_IC_MASTER);
198+
197199
/* If the slave address is ten bit address, enable 10BITADDR */
198200
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
199201
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)
831833
ret = -EIO;
832834

833835
done:
836+
i2c_dw_set_mode(dev, DW_IC_SLAVE);
837+
834838
i2c_dw_release_lock(dev);
835839

836840
done_nolock:
@@ -853,7 +857,7 @@ void i2c_dw_configure_master(struct dw_i2c_dev *dev)
853857
{
854858
struct i2c_timings *t = &dev->timings;
855859

856-
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
860+
dev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
857861

858862
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
859863
DW_IC_CON_RESTART_EN;

drivers/i2c/busses/i2c-designware-slave.c

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,25 @@
2424
int i2c_dw_reg_slave(struct i2c_client *slave)
2525
{
2626
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
27+
int ret;
2728

29+
if (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE))
30+
return -EOPNOTSUPP;
2831
if (dev->slave)
2932
return -EBUSY;
3033
if (slave->flags & I2C_CLIENT_TEN)
3134
return -EAFNOSUPPORT;
32-
pm_runtime_get_sync(dev->dev);
3335

34-
/*
35-
* Set slave address in the IC_SAR register,
36-
* the address to which the DW_apb_i2c responds.
37-
*/
36+
ret = i2c_dw_acquire_lock(dev);
37+
if (ret)
38+
return ret;
39+
40+
pm_runtime_get_sync(dev->dev);
3841
__i2c_dw_disable_nowait(dev);
39-
regmap_write(dev->map, DW_IC_SAR, slave->addr);
4042
dev->slave = slave;
43+
i2c_dw_set_mode(dev, DW_IC_SLAVE);
4144

42-
__i2c_dw_enable(dev);
43-
44-
dev->status = 0;
45+
i2c_dw_release_lock(dev);
4546

4647
return 0;
4748
}
@@ -54,6 +55,7 @@ int i2c_dw_unreg_slave(struct i2c_client *slave)
5455
i2c_dw_disable(dev);
5556
synchronize_irq(dev->irq);
5657
dev->slave = NULL;
58+
i2c_dw_set_mode(dev, DW_IC_MASTER);
5759
pm_runtime_put_sync_suspend(dev->dev);
5860

5961
return 0;
@@ -176,23 +178,16 @@ irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev)
176178

177179
void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
178180
{
179-
dev->functionality = I2C_FUNC_SLAVE;
181+
if (dev->flags & ACCESS_POLLING)
182+
return;
183+
184+
dev->functionality |= I2C_FUNC_SLAVE;
180185

181186
dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
182187
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
183-
184-
dev->mode = DW_IC_SLAVE;
185188
}
186189
EXPORT_SYMBOL_GPL(i2c_dw_configure_slave);
187190

188-
int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
189-
{
190-
if (dev->flags & ACCESS_POLLING)
191-
return -EOPNOTSUPP;
192-
193-
return 0;
194-
}
195-
196191
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
197192
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");
198193
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)