Commit 445094c8 authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Wolfram Sang
Browse files

i2c: exynos5: add support for atomic transfers



Add support for atomic transfers using polling mode with interrupts
intentionally disabled. This removes the warning introduced by commit
63b96983 ("i2c: core: introduce callbacks for atomic transfers")
during system reboot and power-off.

Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: default avatarAndi Shyti <andi.shyti@kernel.org>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 29c9e85d
Loading
Loading
Loading
Loading
+44 −2
Original line number Diff line number Diff line
@@ -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);

	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,
};