mirror of
https://github.com/linux-sunxi/meta-sunxi.git
synced 2024-11-16 10:18:23 +01:00
ce4a0294c3
For details please see: https://github.com/linux-sunxi/meta-sunxi/issues/397 Signed-off-by: Marek Belisko <marek.belisko@open-nandra.com>
146 lines
5.0 KiB
Diff
146 lines
5.0 KiB
Diff
From 544a8d75f3d6e60e160cd92dc56321484598a993 Mon Sep 17 00:00:00 2001
|
|
From: Chris Morgan <macromorgan@hotmail.com>
|
|
Date: Wed, 30 Mar 2022 12:16:57 -0500
|
|
Subject: [PATCH] i2c: mv64xxx: Add atomic_xfer method to driver
|
|
|
|
Add an atomic_xfer method to the driver so that it behaves correctly
|
|
when controlling a PMIC that is responsible for device shutdown.
|
|
|
|
The atomic_xfer method added is similar to the one from the i2c-rk3x
|
|
driver. When running an atomic_xfer a bool flag in the driver data is
|
|
set, the interrupt is not unmasked on transfer start, and the IRQ
|
|
handler is manually invoked while waiting for pending transfers to
|
|
complete.
|
|
|
|
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
|
|
Acked-by: Gregory CLEMENT <gregory.clement@bootlin.com>
|
|
Signed-off-by: Wolfram Sang <wsa@kernel.org>
|
|
---
|
|
drivers/i2c/busses/i2c-mv64xxx.c | 52 ++++++++++++++++++++++++++++----
|
|
1 file 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,
|
|
};
|
|
|
|
--
|
|
2.25.1
|
|
|