diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-mv64xxx.c | 52 |
1 files changed, 46 insertions, 6 deletions
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 424c53e4c513..103a05ecc3d6 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -150,6 +150,7 @@ struct mv64xxx_i2c_data { /* Clk div is 2 to the power n, not 2 to the power n + 1 */ bool clk_n_base_0; struct i2c_bus_recovery_info rinfo; + bool atomic; }; static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { @@ -179,7 +180,10 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, u32 dir = 0; drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | - MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN; + MV64XXX_I2C_REG_CONTROL_TWSIEN; + + if (!drv_data->atomic) + drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_INTEN; if (msg->flags & I2C_M_RD) dir = 1; @@ -409,7 +413,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) case MV64XXX_I2C_ACTION_RCV_DATA_STOP: drv_data->msg->buf[drv_data->byte_posn++] = readl(drv_data->reg_base + drv_data->reg_offsets.data); - drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + if (!drv_data->atomic) + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block = 0; @@ -427,7 +432,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->rc = -EIO; fallthrough; case MV64XXX_I2C_ACTION_SEND_STOP: - drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + if (!drv_data->atomic) + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block = 0; @@ -575,6 +581,17 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) spin_unlock_irqrestore(&drv_data->lock, flags); } +static void mv64xxx_i2c_wait_polling(struct mv64xxx_i2c_data *drv_data) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), drv_data->adapter.timeout); + + while (READ_ONCE(drv_data->block) && + ktime_compare(ktime_get(), timeout) < 0) { + udelay(5); + mv64xxx_i2c_intr(0, drv_data); + } +} + static int mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, int is_last) @@ -590,7 +607,11 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, mv64xxx_i2c_send_start(drv_data); spin_unlock_irqrestore(&drv_data->lock, flags); - mv64xxx_i2c_wait_for_completion(drv_data); + if (!drv_data->atomic) + mv64xxx_i2c_wait_for_completion(drv_data); + else + mv64xxx_i2c_wait_polling(drv_data); + return drv_data->rc; } @@ -717,7 +738,7 @@ mv64xxx_i2c_functionality(struct i2c_adapter *adap) } static int -mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); int rc, ret = num; @@ -730,7 +751,7 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) drv_data->msgs = msgs; drv_data->num_msgs = num; - if (mv64xxx_i2c_can_offload(drv_data)) + if (mv64xxx_i2c_can_offload(drv_data) && !drv_data->atomic) rc = mv64xxx_i2c_offload_xfer(drv_data); else rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); @@ -747,8 +768,27 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) return ret; } +static int +mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); + + drv_data->atomic = 0; + return mv64xxx_i2c_xfer_core(adap, msgs, num); +} + +static int mv64xxx_i2c_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); + + drv_data->atomic = 1; + return mv64xxx_i2c_xfer_core(adap, msgs, num); +} + static const struct i2c_algorithm mv64xxx_i2c_algo = { .master_xfer = mv64xxx_i2c_xfer, + .master_xfer_atomic = mv64xxx_i2c_xfer_atomic, .functionality = mv64xxx_i2c_functionality, }; |