[PATCH v7 4/6] i2c: wmt: fix a bug when thread blocked

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



v6->v7:
	1. some dirty patches were removed
	2. rename structure member 'to/ti' to 't1/t2'
	   to make it easier to understand.
	3. add a comment about arbitration.
	Link: https://lore.kernel.org/all/b0f284621b6763c32133d39be83f05f1184b3635.1703830854.git.hanshu-oc@xxxxxxxxxxx/

During each byte access, the host performs clock stretching.
In this case, the thread may be interrupted by preemption,
resulting in a long stretching time.

However, some touchpad can only tolerate host clock stretching
of no more than 200 ms. We reduce the impact of this through
a retransmission mechanism.

Since __i2c_lock_bus_helper() is used to ensure that the
current access will not be interrupted by the other access,
We don't need to worry about arbitration anymore.

Reviewed-by: Andi Shyti <andi.shyti@xxxxxxxxxx>
Signed-off-by: Hans Hu <hanshu-oc@xxxxxxxxxxx>
---
 drivers/i2c/busses/i2c-viai2c-common.c | 26 ++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-viai2c-common.h |  3 +++
 2 files changed, 29 insertions(+)

diff --git a/drivers/i2c/busses/i2c-viai2c-common.c b/drivers/i2c/busses/i2c-viai2c-common.c
index 3e565d5ee4c7..0fd2554731ca 100644
--- a/drivers/i2c/busses/i2c-viai2c-common.c
+++ b/drivers/i2c/busses/i2c-viai2c-common.c
@@ -22,12 +22,37 @@ int viai2c_check_status(struct viai2c *i2c)
 {
 	int ret = 0;
 	unsigned long time_left;
+	unsigned long delta_ms;
 
 	time_left = wait_for_completion_timeout(&i2c->complete,
 						msecs_to_jiffies(500));
 	if (!time_left)
 		return -ETIMEDOUT;
 
+	/*
+	 * During each byte access, the host performs clock stretching.
+	 * In this case, the thread may be interrupted by preemption,
+	 * resulting in a long stretching time.
+	 *
+	 * However, some touchpad can only tolerate host clock stretching
+	 * of no more than 200 ms. We reduce the impact of this through
+	 * a retransmission mechanism.
+	 *
+	 * Since __i2c_lock_bus_helper() is used to ensure that the
+	 * current access will not be interrupted by the other access,
+	 * We don't need to worry about arbitration anymore.
+	 */
+	local_irq_disable();
+	i2c->t2 = ktime_get();
+	delta_ms = ktime_to_ms(ktime_sub(i2c->t2, i2c->t1));
+	if (delta_ms > VIAI2C_STRETCHING_TIMEOUT) {
+		local_irq_enable();
+		dev_warn(i2c->dev, "thread blocked more than %ldms\n", delta_ms);
+		return -EAGAIN;
+	}
+	i2c->t1 = i2c->t2;
+	local_irq_enable();
+
 	if (i2c->cmd_status & VIAI2C_ISR_NACK_ADDR)
 		ret = -EIO;
 
@@ -158,6 +183,7 @@ int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	int ret = 0;
 	struct viai2c *i2c = i2c_get_adapdata(adap);
 
+	i2c->t2 = i2c->t1 = ktime_get();
 	for (i = 0; ret >= 0 && i < num; i++) {
 		pmsg = &msgs[i];
 		if (!(pmsg->flags & I2C_M_NOSTART)) {
diff --git a/drivers/i2c/busses/i2c-viai2c-common.h b/drivers/i2c/busses/i2c-viai2c-common.h
index 28799e7e97f0..d0ffba22104d 100644
--- a/drivers/i2c/busses/i2c-viai2c-common.h
+++ b/drivers/i2c/busses/i2c-viai2c-common.h
@@ -49,6 +49,7 @@
 #define VIAI2C_REG_MCR		0x0E
 
 #define VIAI2C_TIMEOUT		(msecs_to_jiffies(1000))
+#define VIAI2C_STRETCHING_TIMEOUT	200
 
 struct viai2c {
 	struct i2c_adapter	adapter;
@@ -59,6 +60,8 @@ struct viai2c {
 	u16			tcr;
 	int			irq;
 	u16			cmd_status;
+	ktime_t			t1;
+	ktime_t			t2;
 };
 
 int viai2c_wait_bus_not_busy(struct viai2c *i2c);
-- 
2.34.1





[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux