Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 713a3a94 authored by Ulrich Hecht's avatar Ulrich Hecht Committed by Greg Kroah-Hartman
Browse files

i2c: sh_mobile: implement atomic transfers



[ Upstream commit a49cc1fe9d64a2dc4e19b599204f403e5d25f44b ]

Implements atomic transfers to fix reboot/shutdown on r8a7790 Lager and
similar boards.

Signed-off-by: default avatarUlrich Hecht <uli+renesas@fpond.eu>
Tested-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
[wsa: some whitespace fixing]
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 37a048d7
Loading
Loading
Loading
Loading
+66 −20
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ struct sh_mobile_i2c_data {
	int sr;
	bool send_stop;
	bool stop_after_dma;
	bool atomic_xfer;

	struct resource *res;
	struct dma_chan *dma_tx;
@@ -333,11 +334,13 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op
		ret = iic_rd(pd, ICDR);
		break;
	case OP_RX_STOP: /* enable DTE interrupt, issue stop */
		if (!pd->atomic_xfer)
			iic_wr(pd, ICIC,
			       ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
		iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
		break;
	case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
		if (!pd->atomic_xfer)
			iic_wr(pd, ICIC,
			       ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
		ret = iic_rd(pd, ICDR);
@@ -435,6 +438,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)

	if (wakeup) {
		pd->sr |= SW_DONE;
		if (!pd->atomic_xfer)
			wake_up(&pd->wait);
	}

@@ -587,6 +591,9 @@ static void start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
	pd->pos = -1;
	pd->sr = 0;

	if (pd->atomic_xfer)
		return;

	pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
	if (pd->dma_buf)
		sh_mobile_i2c_xfer_dma(pd);
@@ -643,15 +650,13 @@ static int poll_busy(struct sh_mobile_i2c_data *pd)
	return i ? 0 : -ETIMEDOUT;
}

static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
			      struct i2c_msg *msgs,
			      int num)
static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd,
			 struct i2c_msg *msgs, int num)
{
	struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
	struct i2c_msg	*msg;
	int err = 0;
	int i;
	long timeout;
	long time_left;

	/* Wake up device and enable clock */
	pm_runtime_get_sync(pd->dev);
@@ -668,15 +673,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
		if (do_start)
			i2c_op(pd, OP_START);

		if (pd->atomic_xfer) {
			unsigned long j = jiffies + pd->adap.timeout;

			time_left = time_before_eq(jiffies, j);
			while (time_left &&
			       !(pd->sr & (ICSR_TACK | SW_DONE))) {
				unsigned char sr = iic_rd(pd, ICSR);

				if (sr & (ICSR_AL   | ICSR_TACK |
					  ICSR_WAIT | ICSR_DTE)) {
					sh_mobile_i2c_isr(0, pd);
					udelay(150);
				} else {
					cpu_relax();
				}
				time_left = time_before_eq(jiffies, j);
			}
		} else {
			/* The interrupt handler takes care of the rest... */
		timeout = wait_event_timeout(pd->wait,
			time_left = wait_event_timeout(pd->wait,
					pd->sr & (ICSR_TACK | SW_DONE),
				       adapter->timeout);
					pd->adap.timeout);

		/* 'stop_after_dma' tells if DMA transfer was complete */
		i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg, pd->stop_after_dma);
			/* 'stop_after_dma' tells if DMA xfer was complete */
			i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg,
						 pd->stop_after_dma);
		}

		if (!timeout) {
		if (!time_left) {
			dev_err(pd->dev, "Transfer request timed out\n");
			if (pd->dma_direction != DMA_NONE)
				sh_mobile_i2c_cleanup_dma(pd);
@@ -702,6 +727,26 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
	return err ?: num;
}

static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
			      struct i2c_msg *msgs,
			      int num)
{
	struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);

	pd->atomic_xfer = false;
	return sh_mobile_xfer(pd, msgs, num);
}

static int sh_mobile_i2c_xfer_atomic(struct i2c_adapter *adapter,
				     struct i2c_msg *msgs,
				     int num)
{
	struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);

	pd->atomic_xfer = true;
	return sh_mobile_xfer(pd, msgs, num);
}

static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
@@ -710,6 +755,7 @@ static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
	.functionality = sh_mobile_i2c_func,
	.master_xfer = sh_mobile_i2c_xfer,
	.master_xfer_atomic = sh_mobile_i2c_xfer_atomic,
};

static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {