diff mbox series

[v3] i2c: exynos5: add support for atomic transfers

Message ID 20231006150804.4113844-1-m.szyprowski@samsung.com
State New
Headers show
Series [v3] i2c: exynos5: add support for atomic transfers | expand

Commit Message

Marek Szyprowski Oct. 6, 2023, 3:08 p.m. UTC
Add support for atomic transfers using polling 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>
---
v3:
- simplified timeout calculations, adjusted some names even more

v2:
- adjusted some names as pointed by Andi
---
 drivers/i2c/busses/i2c-exynos5.c | 46 ++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

Comments

Andi Shyti Oct. 11, 2023, 10:10 p.m. UTC | #1
Hi Marek,

...

> +static bool exynos5_i2c_poll_irqs_timeout(struct exynos5_i2c *i2c,
> +					  unsigned long timeout)
> +{
> +	unsigned long time_left = jiffies + timeout;
> +
> +	while (time_before(jiffies, time_left) &&
> +	       !((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);
> +	}
> +	return time_before(jiffies, time_left);
> +}
> +
>  static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
>  			      struct i2c_msg *msgs, int stop)
>  {
> @@ -725,8 +746,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->atomic)
> +		timeout = wait_for_completion_timeout(&i2c->msg_complete,
> +						      EXYNOS5_I2C_TIMEOUT);
> +	else
> +		timeout = exynos5_i2c_poll_irqs_timeout(i2c,
> +							EXYNOS5_I2C_TIMEOUT);

what's a bit bothering here is that one function returns a
boolean while the other returns a timeout stored in an unsigned
long.

Anyway, exynos5_i2c_poll_irqs_timeout() is used only here as a
boolean, so that I guess it's fine.

Reviewed-by: Andi Shyti <andi.shyti@kernel.org> 

Andi
Wolfram Sang Oct. 21, 2023, 6:44 p.m. UTC | #2
On Fri, Oct 06, 2023 at 05:08:03PM +0200, Marek Szyprowski wrote:
> Add support for atomic transfers using polling 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>

Applied to for-next, thanks!
Wolfram Sang Oct. 21, 2023, 7:55 p.m. UTC | #3
On Sat, Oct 21, 2023 at 08:44:55PM +0200, Wolfram Sang wrote:
> On Fri, Oct 06, 2023 at 05:08:03PM +0200, Marek Szyprowski wrote:
> > Add support for atomic transfers using polling 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>
> 
> Applied to for-next, thanks!

Marek, since you have hardware, are you maybe interested in reviewing /
testing this patch?

http://patchwork.ozlabs.org/project/linux-i2c/patch/20220912085943.1098651-1-camel.guo@axis.com/
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 2b0b9cdffa86..65cb06ec3804 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -194,6 +194,11 @@  struct exynos5_i2c {
 	 */
 	int			trans_done;
 
+	/*
+	 * Called from atomic context, don't use interrupts.
+	 */
+	unsigned int		atomic;
+
 	/* Controller operating frequency */
 	unsigned int		op_clock;
 
@@ -711,6 +716,22 @@  static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
 	spin_unlock_irqrestore(&i2c->lock, flags);
 }
 
+static bool exynos5_i2c_poll_irqs_timeout(struct exynos5_i2c *i2c,
+					  unsigned long timeout)
+{
+	unsigned long time_left = jiffies + timeout;
+
+	while (time_before(jiffies, time_left) &&
+	       !((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);
+	}
+	return time_before(jiffies, time_left);
+}
+
 static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
 			      struct i2c_msg *msgs, int stop)
 {
@@ -725,8 +746,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->atomic)
+		timeout = wait_for_completion_timeout(&i2c->msg_complete,
+						      EXYNOS5_I2C_TIMEOUT);
+	else
+		timeout = exynos5_i2c_poll_irqs_timeout(i2c,
+							EXYNOS5_I2C_TIMEOUT);
+
 	if (timeout == 0)
 		ret = -ETIMEDOUT;
 	else
@@ -777,6 +803,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->atomic = true;
+	ret = exynos5_i2c_xfer(adap, msgs, num);
+	i2c->atomic = 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 +825,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,
 };