Commit 5190417b authored by Matthias Schiffer's avatar Matthias Schiffer Committed by Wolfram Sang
Browse files

i2c: mxs: ensure that DMA buffers are safe for DMA



We found that after commit 9c46929e
("ARM: implement THREAD_INFO_IN_TASK for uniprocessor systems"), the
PCF85063 RTC driver stopped working on i.MX28 due to regmap_bulk_read()
reading bogus data into a stack buffer. This is caused by the i2c-mxs
driver using DMA transfers even for messages without the I2C_M_DMA_SAFE
flag, and the aforementioned commit enabling vmapped stacks.

As the MXS I2C controller requires DMA for reads of >4 bytes, DMA can't be
disabled, so the issue is fixed by using i2c_get_dma_safe_msg_buf() to
create a bounce buffer when needed.

Fixes: 9c46929e ("ARM: implement THREAD_INFO_IN_TASK for uniprocessor systems")
Signed-off-by: default avatarMatthias Schiffer <matthias.schiffer@ew.tq-group.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 1c788500
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -171,7 +171,7 @@ static void mxs_i2c_dma_irq_callback(void *param)
}

static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msg, uint32_t flags)
			struct i2c_msg *msg, u8 *buf, uint32_t flags)
{
	struct dma_async_tx_descriptor *desc;
	struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
@@ -226,7 +226,7 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
		}

		/* Queue the DMA data transfer. */
		sg_init_one(&i2c->sg_io[1], msg->buf, msg->len);
		sg_init_one(&i2c->sg_io[1], buf, msg->len);
		dma_map_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE);
		desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[1], 1,
					DMA_DEV_TO_MEM,
@@ -259,7 +259,7 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
		/* Queue the DMA data transfer. */
		sg_init_table(i2c->sg_io, 2);
		sg_set_buf(&i2c->sg_io[0], &i2c->addr_data, 1);
		sg_set_buf(&i2c->sg_io[1], msg->buf, msg->len);
		sg_set_buf(&i2c->sg_io[1], buf, msg->len);
		dma_map_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
		desc = dmaengine_prep_slave_sg(i2c->dmach, i2c->sg_io, 2,
					DMA_MEM_TO_DEV,
@@ -563,6 +563,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
	struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
	int ret;
	int flags;
	u8 *dma_buf;
	int use_pio = 0;
	unsigned long time_left;

@@ -588,13 +589,20 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
		if (ret && (ret != -ENXIO))
			mxs_i2c_reset(i2c);
	} else {
		dma_buf = i2c_get_dma_safe_msg_buf(msg, 1);
		if (!dma_buf)
			return -ENOMEM;

		reinit_completion(&i2c->cmd_complete);
		ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
		if (ret)
		ret = mxs_i2c_dma_setup_xfer(adap, msg, dma_buf, flags);
		if (ret) {
			i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
			return ret;
		}

		time_left = wait_for_completion_timeout(&i2c->cmd_complete,
						msecs_to_jiffies(1000));
		i2c_put_dma_safe_msg_buf(dma_buf, msg, true);
		if (!time_left)
			goto timeout;