summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c52
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,
};