diff mbox series

i2c: exynos5: add support for atomic transfers

Message ID 20230929113841.1272625-1-m.szyprowski@samsung.com
State Superseded
Headers show
Series i2c: exynos5: add support for atomic transfers | expand

Commit Message

Marek Szyprowski Sept. 29, 2023, 11:38 a.m. UTC
Add support for atomic transfers using polled mode with interrupts
intentionally disabled. This removes the warning introduced by commit
63b96983a5dd ("i2c: core: introduce callbacks for atomic transfers")
during system reboot and power off.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/i2c/busses/i2c-exynos5.c | 47 ++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 3 deletions(-)

Comments

Andi Shyti Sept. 29, 2023, 10:57 p.m. UTC | #1
Hi Marek,

> @@ -178,6 +179,7 @@ struct exynos5_i2c {
>  	unsigned int		msg_ptr;
>  
>  	unsigned int		irq;
> +	unsigned int		polled;

Is this supposed to be called polling?

>  	void __iomem		*regs;
>  	struct clk		*clk;		/* operating clock */
> @@ -711,6 +713,24 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
>  	spin_unlock_irqrestore(&i2c->lock, flags);
>  }
>  
> +static unsigned long exynos5_i2c_polled_irqs_timeout(struct exynos5_i2c *i2c,
> +						     unsigned long timeout_ms)
> +{
> +	ktime_t start, now;
> +
> +	start = now = ktime_get();
> +	while (ktime_ms_delta(now, start) < timeout_ms &&
> +	       !((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
> +	         (i2c->state < 0))) {
> +		while (readl(i2c->regs + HSI2C_INT_ENABLE) &
> +		       readl(i2c->regs + HSI2C_INT_STATUS))
> +			exynos5_i2c_irq(i2c->irq, i2c);
> +		usleep_range(100, 200);
> +		now = ktime_get();
> +	}
> +	return ktime_ms_delta(now, start) < timeout_ms;

what are you returning here?

Andi
Marek Szyprowski Oct. 2, 2023, 5:37 a.m. UTC | #2
On 30.09.2023 00:57, Andi Shyti wrote:
>> @@ -178,6 +179,7 @@ struct exynos5_i2c {
>>   	unsigned int		msg_ptr;
>>   
>>   	unsigned int		irq;
>> +	unsigned int		polled;
> Is this supposed to be called polling?

Yea, a bit better name.

>>   	void __iomem		*regs;
>>   	struct clk		*clk;		/* operating clock */
>> @@ -711,6 +713,24 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
>>   	spin_unlock_irqrestore(&i2c->lock, flags);
>>   }
>>   
>> +static unsigned long exynos5_i2c_polled_irqs_timeout(struct exynos5_i2c *i2c,
>> +						     unsigned long timeout_ms)
>> +{
>> +	ktime_t start, now;
>> +
>> +	start = now = ktime_get();
>> +	while (ktime_ms_delta(now, start) < timeout_ms &&
>> +	       !((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
>> +	         (i2c->state < 0))) {
>> +		while (readl(i2c->regs + HSI2C_INT_ENABLE) &
>> +		       readl(i2c->regs + HSI2C_INT_STATUS))
>> +			exynos5_i2c_irq(i2c->irq, i2c);
>> +		usleep_range(100, 200);
>> +		now = ktime_get();
>> +	}
>> +	return ktime_ms_delta(now, start) < timeout_ms;
> what are you returning here?

Values similar to wait_for_completion_timeout(); 0 means timeout and 
non-zero that the waiting condition has been reached, please check how 
it is used in exynos5_i2c_xfer_msg(). Maybe the function should be named 
a bit different, but I had no good idea so far.

Best regards
Andi Shyti Oct. 3, 2023, 5:16 p.m. UTC | #3
Hi Marek,

> >> +static unsigned long exynos5_i2c_polled_irqs_timeout(struct exynos5_i2c *i2c,
> >> +						     unsigned long timeout_ms)
> >> +{
> >> +	ktime_t start, now;
> >> +
> >> +	start = now = ktime_get();
> >> +	while (ktime_ms_delta(now, start) < timeout_ms &&
> >> +	       !((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
> >> +	         (i2c->state < 0))) {
> >> +		while (readl(i2c->regs + HSI2C_INT_ENABLE) &
> >> +		       readl(i2c->regs + HSI2C_INT_STATUS))
> >> +			exynos5_i2c_irq(i2c->irq, i2c);
> >> +		usleep_range(100, 200);
> >> +		now = ktime_get();
> >> +	}
> >> +	return ktime_ms_delta(now, start) < timeout_ms;
> > what are you returning here?
> 
> Values similar to wait_for_completion_timeout(); 0 means timeout and 
> non-zero that the waiting condition has been reached, please check how 
> it is used in exynos5_i2c_xfer_msg(). Maybe the function should be named 
> a bit different, but I had no good idea so far.

but you are returning a boolean here. Make it a boolean then, no?

It's true that the timeout is treated as a 1/0, but still it's a
bit misleading.

Andi
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 2b0b9cdffa86..4b012d86d811 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -162,7 +162,8 @@ 
 #define HSI2C_MASTER_ID(x)			((x & 0xff) << 24)
 #define MASTER_ID(x)				((x & 0x7) + 0x08)
 
-#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100))
+#define EXYNOS5_I2C_TIMEOUT_MS (100)
+#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(EXYNOS5_I2C_TIMEOUT_MS))
 
 enum i2c_type_exynos {
 	I2C_TYPE_EXYNOS5,
@@ -178,6 +179,7 @@  struct exynos5_i2c {
 	unsigned int		msg_ptr;
 
 	unsigned int		irq;
+	unsigned int		polled;
 
 	void __iomem		*regs;
 	struct clk		*clk;		/* operating clock */
@@ -711,6 +713,24 @@  static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
 	spin_unlock_irqrestore(&i2c->lock, flags);
 }
 
+static unsigned long exynos5_i2c_polled_irqs_timeout(struct exynos5_i2c *i2c,
+						     unsigned long timeout_ms)
+{
+	ktime_t start, now;
+
+	start = now = ktime_get();
+	while (ktime_ms_delta(now, start) < timeout_ms &&
+	       !((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
+	         (i2c->state < 0))) {
+		while (readl(i2c->regs + HSI2C_INT_ENABLE) &
+		       readl(i2c->regs + HSI2C_INT_STATUS))
+			exynos5_i2c_irq(i2c->irq, i2c);
+		usleep_range(100, 200);
+		now = ktime_get();
+	}
+	return ktime_ms_delta(now, start) < timeout_ms;
+}
+
 static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
 			      struct i2c_msg *msgs, int stop)
 {
@@ -725,8 +745,13 @@  static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
 
 	exynos5_i2c_message_start(i2c, stop);
 
-	timeout = wait_for_completion_timeout(&i2c->msg_complete,
-					      EXYNOS5_I2C_TIMEOUT);
+	if (!i2c->polled)
+		timeout = wait_for_completion_timeout(&i2c->msg_complete,
+						      EXYNOS5_I2C_TIMEOUT);
+	else
+		timeout = exynos5_i2c_polled_irqs_timeout(i2c,
+							EXYNOS5_I2C_TIMEOUT_MS);
+
 	if (timeout == 0)
 		ret = -ETIMEDOUT;
 	else
@@ -777,6 +802,21 @@  static int exynos5_i2c_xfer(struct i2c_adapter *adap,
 	return ret ?: num;
 }
 
+static int exynos5_i2c_xfer_atomic(struct i2c_adapter *adap,
+				   struct i2c_msg *msgs, int num)
+{
+	struct exynos5_i2c *i2c = adap->algo_data;
+	int ret;
+
+	disable_irq(i2c->irq);
+	i2c->polled = true;
+	ret = exynos5_i2c_xfer(adap, msgs, num);
+	i2c->polled = false;
+	enable_irq(i2c->irq);
+
+	return ret;
+}
+
 static u32 exynos5_i2c_func(struct i2c_adapter *adap)
 {
 	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
@@ -784,6 +824,7 @@  static u32 exynos5_i2c_func(struct i2c_adapter *adap)
 
 static const struct i2c_algorithm exynos5_i2c_algorithm = {
 	.master_xfer		= exynos5_i2c_xfer,
+	.master_xfer_atomic	= exynos5_i2c_xfer_atomic,
 	.functionality		= exynos5_i2c_func,
 };