diff mbox series

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

Message ID f56773092681736140447f47962aa2f6c3df3773.1704440251.git.hanshu-oc@zhaoxin.com
State New
Headers show
Series i2c: add zhaoxin i2c controller driver | expand

Commit Message

Hans Hu Jan. 5, 2024, 7:51 a.m. UTC
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@zhaoxin.com/

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@kernel.org>
Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com>
---
 drivers/i2c/busses/i2c-viai2c-common.c | 26 ++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-viai2c-common.h |  3 +++
 2 files changed, 29 insertions(+)

Comments

Wolfram Sang Feb. 21, 2024, 12:37 p.m. UTC | #1
Hi,

On Fri, Jan 05, 2024 at 03:51:47PM +0800, Hans Hu wrote:
> 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@zhaoxin.com/
> 
> 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@kernel.org>
> Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com>

Uh oh, NACK. We shouldn't limit clock stretching because some touchpad
controllers can't handle it.

The first thing I suggest is to move more handling to the interrupt
context, like filling the next byte after the previous has been
processed. Then, you are not interruptible anymore.

If this all fails, we need to determine a bus specific property, but I
am quite sure the above conversion will be enough.

Maybe it is an idea to first get the driver converted to support your
platform, and afterwards the conversion to more handling in interrupt.

Kind regards,

   Wolfram
Hans Hu Feb. 22, 2024, 9:03 a.m. UTC | #2
Hi Wolfram,


On 2024/2/21 20:37, Wolfram Sang wrote:
> Hi,
>
> On Fri, Jan 05, 2024 at 03:51:47PM +0800, Hans Hu wrote:
>> 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@zhaoxin.com/
>>
>> 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@kernel.org>
>> Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com>
> Uh oh, NACK. We shouldn't limit clock stretching because some touchpad
> controllers can't handle it.
>
> The first thing I suggest is to move more handling to the interrupt
> context, like filling the next byte after the previous has been
> processed. Then, you are not interruptible anymore.
>
> If this all fails, we need to determine a bus specific property, but I
> am quite sure the above conversion will be enough.
>
> Maybe it is an idea to first get the driver converted to support your
> platform, and afterwards the conversion to more handling in interrupt.
>
> Kind regards,
>
>     Wolfram
>

Thanks for your suggestion, the purpose of this approach is to
reduce the clock stretching caused by the system. Therefore,
I try to put almost all of the processing in the interrupt context.

Hans,
Thank you
Hans Hu Feb. 22, 2024, 10:42 a.m. UTC | #3
>> Thanks for your suggestion, the purpose of this approach is to
>> reduce the clock stretching caused by the system. Therefore,
>> I try to put almost all of the processing in the interrupt context.
> Well, I think per-msg handling in interrupt context is enough. The
> transfer (consisting of multiple messages) handling is usually best left
> in process context.
>

OK, I understand now.

Hans,
Thank you
Wolfram Sang Feb. 22, 2024, 5:26 p.m. UTC | #4
On Thu, Feb 22, 2024 at 06:42:20PM +0800, Hans Hu wrote:
> 
> > > Thanks for your suggestion, the purpose of this approach is to
> > > reduce the clock stretching caused by the system. Therefore,
> > > I try to put almost all of the processing in the interrupt context.
> > Well, I think per-msg handling in interrupt context is enough. The
> > transfer (consisting of multiple messages) handling is usually best left
> > in process context.
> > 
> 
> OK, I understand now.

If you need inspiration, check i2c-digicolor.c for a simple driver
implementing it.
diff mbox series

Patch

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);