All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 1/4] i2c: tegra: Sort all the include headers alphabetically
@ 2019-01-24 20:51 ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch sorts all the include headers alphabetically for the
I2C tegra driver

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Added this in V2 to sort the headers in tegra I2C

 drivers/i2c/busses/i2c-tegra.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index e417ebf7628c..ef854be4c837 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -6,24 +6,23 @@
  * Author: Colin Cross <ccross@android.com>
  */
 
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <asm/unaligned.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
-#include <linux/io.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/reset.h>
+#include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/iopoll.h>
-
-#include <asm/unaligned.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
 
 #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
 #define BYTES_PER_FIFO_WORD 4
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 1/4] i2c: tegra: Sort all the include headers alphabetically
@ 2019-01-24 20:51 ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch sorts all the include headers alphabetically for the
I2C tegra driver

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Added this in V2 to sort the headers in tegra I2C

 drivers/i2c/busses/i2c-tegra.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index e417ebf7628c..ef854be4c837 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -6,24 +6,23 @@
  * Author: Colin Cross <ccross@android.com>
  */
 
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <asm/unaligned.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
-#include <linux/io.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/reset.h>
+#include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/iopoll.h>
-
-#include <asm/unaligned.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
 
 #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
 #define BYTES_PER_FIFO_WORD 4
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-24 20:51 ` Sowjanya Komatineni
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  -1 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch prepares the buffer with the message bytes to be
transmitted along with the packet header information and then
performs i2c transfer in PIO mode.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : DMA support changes include preparing buffer with message bytes and
	and header before sending them through DMA. So splitted the whole
	change into 2 seperate patches in this series.

 drivers/i2c/busses/i2c-tegra.c | 97 +++++++++++++++++++++++++++++-------------
 1 file changed, 68 insertions(+), 29 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index ef854be4c837..13bce1411ddc 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -117,6 +117,9 @@
 #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
 #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
 
+/* Packet header size in bytes */
+#define I2C_PACKET_HEADER_SIZE			12
+
 /*
  * msg_end_type: The bus control which need to be send at end of transfer.
  * @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
+{
+	u32 *buffer = (u32 *)i2c_dev->msg_buf;
+	unsigned long flags;
+	u32 int_mask;
+
+	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
+
+	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+
+	i2c_dev->msg_buf = (u8 *) buffer;
+
+	if (!i2c_dev->msg_read)
+		tegra_i2c_fill_tx_fifo(i2c_dev);
+
+	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
+		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
+	if (i2c_dev->msg_read)
+		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+	else if (i2c_dev->msg_buf_remaining)
+		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+		i2c_readl(i2c_dev, I2C_INT_MASK));
+
+	return 0;
+}
+
 static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	struct i2c_msg *msg, enum msg_end_type end_state)
 {
 	u32 packet_header;
 	u32 int_mask;
 	unsigned long time_left;
-	unsigned long flags;
+	u32 *buffer;
+	int ret = 0;
+
+	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
+					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
 
 	tegra_i2c_flush_fifos(i2c_dev);
 
-	i2c_dev->msg_buf = msg->buf;
+	i2c_dev->msg_buf = (u8 *)buffer;
 	i2c_dev->msg_buf_remaining = msg->len;
 	i2c_dev->msg_err = I2C_ERR_NONE;
 	i2c_dev->msg_read = (msg->flags & I2C_M_RD);
 	reinit_completion(&i2c_dev->msg_complete);
 
-	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
-
-	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
-	tegra_i2c_unmask_irq(i2c_dev, int_mask);
-
-	packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+	(*buffer++) = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
 			PACKET_HEADER0_PROTOCOL_I2C |
 			(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
 			(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
 
-	packet_header = msg->len - 1;
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+	(*buffer++) = msg->len - 1;
 
 	packet_header = I2C_HEADER_IE_ENABLE;
 	if (end_state == MSG_END_CONTINUE)
@@ -722,34 +759,31 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		packet_header |= I2C_HEADER_CONT_ON_NAK;
 	if (msg->flags & I2C_M_RD)
 		packet_header |= I2C_HEADER_READ;
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+	*(buffer++) = packet_header;
 
 	if (!(msg->flags & I2C_M_RD))
-		tegra_i2c_fill_tx_fifo(i2c_dev);
-
-	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
-		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
-	if (msg->flags & I2C_M_RD)
-		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
-	else if (i2c_dev->msg_buf_remaining)
-		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+		memcpy(buffer, msg->buf, msg->len);
 
-	tegra_i2c_unmask_irq(i2c_dev, int_mask);
-	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
-	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
-		i2c_readl(i2c_dev, I2C_INT_MASK));
+	tegra_i2c_start_pio_xfer(i2c_dev);
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
 						TEGRA_I2C_TIMEOUT);
-	tegra_i2c_mask_irq(i2c_dev, int_mask);
-
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 
 		tegra_i2c_init(i2c_dev);
-		return -ETIMEDOUT;
+		ret = -ETIMEDOUT;
+		goto err_xfer;
+	}
+
+	if (i2c_dev->msg_read) {
+		i2c_dev->msg_buf = (u8 *)buffer;
+		memcpy(msg->buf, i2c_dev->msg_buf, msg->len);
 	}
 
+	int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
+	tegra_i2c_mask_irq(i2c_dev, int_mask);
+
 	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
 		time_left, completion_done(&i2c_dev->msg_complete),
 		i2c_dev->msg_err);
@@ -761,10 +795,15 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
 		if (msg->flags & I2C_M_IGNORE_NAK)
 			return 0;
-		return -EREMOTEIO;
+		ret = -EREMOTEIO;
+		goto err_xfer;
 	}
 
-	return -EIO;
+	ret = -EIO;
+
+err_xfer:
+	kfree(buffer);
+	return ret;
 }
 
 static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch prepares the buffer with the message bytes to be
transmitted along with the packet header information and then
performs i2c transfer in PIO mode.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : DMA support changes include preparing buffer with message bytes and
	and header before sending them through DMA. So splitted the whole
	change into 2 seperate patches in this series.

 drivers/i2c/busses/i2c-tegra.c | 97 +++++++++++++++++++++++++++++-------------
 1 file changed, 68 insertions(+), 29 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index ef854be4c837..13bce1411ddc 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -117,6 +117,9 @@
 #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
 #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
 
+/* Packet header size in bytes */
+#define I2C_PACKET_HEADER_SIZE			12
+
 /*
  * msg_end_type: The bus control which need to be send at end of transfer.
  * @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
+{
+	u32 *buffer = (u32 *)i2c_dev->msg_buf;
+	unsigned long flags;
+	u32 int_mask;
+
+	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
+
+	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+
+	i2c_dev->msg_buf = (u8 *) buffer;
+
+	if (!i2c_dev->msg_read)
+		tegra_i2c_fill_tx_fifo(i2c_dev);
+
+	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
+		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
+	if (i2c_dev->msg_read)
+		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+	else if (i2c_dev->msg_buf_remaining)
+		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+		i2c_readl(i2c_dev, I2C_INT_MASK));
+
+	return 0;
+}
+
 static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	struct i2c_msg *msg, enum msg_end_type end_state)
 {
 	u32 packet_header;
 	u32 int_mask;
 	unsigned long time_left;
-	unsigned long flags;
+	u32 *buffer;
+	int ret = 0;
+
+	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
+					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
 
 	tegra_i2c_flush_fifos(i2c_dev);
 
-	i2c_dev->msg_buf = msg->buf;
+	i2c_dev->msg_buf = (u8 *)buffer;
 	i2c_dev->msg_buf_remaining = msg->len;
 	i2c_dev->msg_err = I2C_ERR_NONE;
 	i2c_dev->msg_read = (msg->flags & I2C_M_RD);
 	reinit_completion(&i2c_dev->msg_complete);
 
-	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
-
-	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
-	tegra_i2c_unmask_irq(i2c_dev, int_mask);
-
-	packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+	(*buffer++) = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
 			PACKET_HEADER0_PROTOCOL_I2C |
 			(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
 			(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
 
-	packet_header = msg->len - 1;
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+	(*buffer++) = msg->len - 1;
 
 	packet_header = I2C_HEADER_IE_ENABLE;
 	if (end_state == MSG_END_CONTINUE)
@@ -722,34 +759,31 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		packet_header |= I2C_HEADER_CONT_ON_NAK;
 	if (msg->flags & I2C_M_RD)
 		packet_header |= I2C_HEADER_READ;
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+	*(buffer++) = packet_header;
 
 	if (!(msg->flags & I2C_M_RD))
-		tegra_i2c_fill_tx_fifo(i2c_dev);
-
-	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
-		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
-	if (msg->flags & I2C_M_RD)
-		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
-	else if (i2c_dev->msg_buf_remaining)
-		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+		memcpy(buffer, msg->buf, msg->len);
 
-	tegra_i2c_unmask_irq(i2c_dev, int_mask);
-	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
-	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
-		i2c_readl(i2c_dev, I2C_INT_MASK));
+	tegra_i2c_start_pio_xfer(i2c_dev);
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
 						TEGRA_I2C_TIMEOUT);
-	tegra_i2c_mask_irq(i2c_dev, int_mask);
-
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 
 		tegra_i2c_init(i2c_dev);
-		return -ETIMEDOUT;
+		ret = -ETIMEDOUT;
+		goto err_xfer;
+	}
+
+	if (i2c_dev->msg_read) {
+		i2c_dev->msg_buf = (u8 *)buffer;
+		memcpy(msg->buf, i2c_dev->msg_buf, msg->len);
 	}
 
+	int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
+	tegra_i2c_mask_irq(i2c_dev, int_mask);
+
 	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
 		time_left, completion_done(&i2c_dev->msg_complete),
 		i2c_dev->msg_err);
@@ -761,10 +795,15 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
 		if (msg->flags & I2C_M_IGNORE_NAK)
 			return 0;
-		return -EREMOTEIO;
+		ret = -EREMOTEIO;
+		goto err_xfer;
 	}
 
-	return -EIO;
+	ret = -EIO;
+
+err_xfer:
+	kfree(buffer);
+	return ret;
 }
 
 static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-24 20:51 ` Sowjanya Komatineni
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  -1 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch adds DMA support for Tegra I2C.

Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
transfer size of the max FIFO depth and DMA mode is used for
transfer size higher than max FIFO depth to save CPU overhead.

PIO mode needs full intervention of CPU to fill or empty FIFO's
and also need to service multiple data requests interrupt for the
same transaction adding overhead on CPU for large transfers.

DMA mode is helpful for Large transfers during downloading or
uploading FW over I2C to some external devices.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Updated based on V1 review feedback along with code cleanup for
	proper implementation of DMA.

 drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 349 insertions(+), 17 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 13bce1411ddc..769700d5a7f3 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -9,6 +9,9 @@
 #include <asm/unaligned.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -46,6 +49,8 @@
 #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
 #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
 #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
+#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
+#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
 #define I2C_FIFO_STATUS				0x060
 #define I2C_FIFO_STATUS_TX_MASK			0xF0
 #define I2C_FIFO_STATUS_TX_SHIFT		4
@@ -120,6 +125,16 @@
 /* Packet header size in bytes */
 #define I2C_PACKET_HEADER_SIZE			12
 
+#define DATA_DMA_DIR_TX				(1 << 0)
+#define DATA_DMA_DIR_RX				(1 << 1)
+
+/*
+ * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
+ * above this, controller will use DMA to fill FIFO.
+ * MAX PIO len is 20 bytes excluding packet header.
+ */
+#define I2C_PIO_MODE_MAX_LEN			32
+
 /*
  * msg_end_type: The bus control which need to be send at end of transfer.
  * @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
  * @fast_clk: clock reference for fast clock of I2C controller
  * @rst: reset control for the I2C controller
  * @base: ioremapped registers cookie
+ * @phys_addr: Physical address of I2C base address to use for DMA configuration
  * @cont_id: I2C controller ID, used for packet header
  * @irq: IRQ number of transfer complete interrupt
  * @irq_disabled: used to track whether or not the interrupt is enabled
@@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
  * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
  * @is_multimaster_mode: track if I2C controller is in multi-master mode
  * @xfer_lock: lock to serialize transfer submission and processing
+ * @has_dma: indicated if controller supports DMA
+ * @tx_dma_chan: DMA transmit channel
+ * @rx_dma_chan: DMA receive channel
+ * @dma_phys: handle to DMA resources
+ * @dma_buf: pointer to allocated DMA buffer
+ * @dma_buf_size: DMA buffer size
+ * @is_curr_dma_xfer: indicates active DMA transfer
+ * @dma_complete: DMA completion notifier
  */
 struct tegra_i2c_dev {
 	struct device *dev;
@@ -202,6 +226,7 @@ struct tegra_i2c_dev {
 	struct clk *fast_clk;
 	struct reset_control *rst;
 	void __iomem *base;
+	phys_addr_t phys_addr;
 	int cont_id;
 	int irq;
 	bool irq_disabled;
@@ -215,8 +240,18 @@ struct tegra_i2c_dev {
 	u16 clk_divisor_non_hs_mode;
 	bool is_multimaster_mode;
 	spinlock_t xfer_lock;
+	bool has_dma;
+	struct dma_chan *tx_dma_chan;
+	struct dma_chan *rx_dma_chan;
+	dma_addr_t dma_phys;
+	u32 *dma_buf;
+	unsigned int dma_buf_size;
+	bool is_curr_dma_xfer;
+	struct completion dma_complete;
 };
 
+static struct dma_chan *chan;
+
 static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
 		       unsigned long reg)
 {
@@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
 	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
 }
 
+static void tegra_i2c_dma_complete(void *args)
+{
+	struct tegra_i2c_dev *i2c_dev = args;
+
+	complete(&i2c_dev->dma_complete);
+}
+
+static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
+{
+	struct dma_async_tx_descriptor *dma_desc;
+	enum dma_transfer_direction dir;
+
+	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
+	reinit_completion(&i2c_dev->dma_complete);
+	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
+					       len, dir, DMA_PREP_INTERRUPT |
+					       DMA_CTRL_ACK);
+	if (!dma_desc) {
+		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
+		return -EIO;
+	}
+
+	dma_desc->callback = tegra_i2c_dma_complete;
+	dma_desc->callback_param = i2c_dev;
+	dmaengine_submit(dma_desc);
+	dma_async_issue_pending(chan);
+	return 0;
+}
+
+static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
+				    bool dma_to_memory)
+{
+	struct dma_chan *dma_chan;
+	u32 *dma_buf;
+	dma_addr_t dma_phys;
+	int ret;
+	const char *chan_name = dma_to_memory ? "rx" : "tx";
+
+	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
+	if (IS_ERR(dma_chan))
+		return PTR_ERR(dma_chan);
+
+	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+				     &dma_phys, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(i2c_dev->dev, "Failed to allocate the DMA buffer\n");
+		ret = -ENOMEM;
+		goto scrub;
+	}
+
+	if (dma_to_memory)
+		i2c_dev->rx_dma_chan = dma_chan;
+	else
+		i2c_dev->tx_dma_chan = dma_chan;
+
+	i2c_dev->dma_buf = dma_buf;
+	i2c_dev->dma_phys = dma_phys;
+
+	return 0;
+
+scrub:
+	dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+			  dma_buf, dma_phys);
+	dma_release_channel(dma_chan);
+	return ret;
+}
+
 static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -529,6 +633,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 		return err;
 	}
 
+	i2c_dev->is_curr_dma_xfer = false;
+
 	reset_control_assert(i2c_dev->rst);
 	udelay(2);
 	reset_control_deassert(i2c_dev->rst);
@@ -642,25 +748,45 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 		goto err;
 	}
 
-	if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
-		if (i2c_dev->msg_buf_remaining)
-			tegra_i2c_empty_rx_fifo(i2c_dev);
-		else
-			BUG();
-	}
+	if (!i2c_dev->is_curr_dma_xfer) {
+		if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
+			if (i2c_dev->msg_buf_remaining)
+				tegra_i2c_empty_rx_fifo(i2c_dev);
+			else
+				BUG();
+		}
 
-	if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
-		if (i2c_dev->msg_buf_remaining)
-			tegra_i2c_fill_tx_fifo(i2c_dev);
-		else
-			tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
+		if (!i2c_dev->msg_read &&
+		   (status & I2C_INT_TX_FIFO_DATA_REQ)) {
+			if (i2c_dev->msg_buf_remaining)
+				tegra_i2c_fill_tx_fifo(i2c_dev);
+			else
+				tegra_i2c_mask_irq(i2c_dev,
+						   I2C_INT_TX_FIFO_DATA_REQ);
+		}
 	}
 
 	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
-	if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+	if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) {
+	/*
+	 * During message read XFER_COMPLETE interrupt is triggered prior to
+	 * DMA complete notification and during message write XFER_COMPLETE
+	 * interrupt is triggered after DMA complete notification.
+	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
+	 * so forcing msg_buf_remaining to 0.
+	 */
+		if (i2c_dev->is_curr_dma_xfer)
+			i2c_dev->msg_buf_remaining = 0;
+		status |= I2C_INT_PACKET_XFER_COMPLETE;
+		i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+		if (!i2c_dev->msg_buf_remaining)
+			complete(&i2c_dev->msg_complete);
+	} else if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+		if (i2c_dev->is_curr_dma_xfer)
+			i2c_dev->msg_buf_remaining = 0;
 		BUG_ON(i2c_dev->msg_buf_remaining);
 		complete(&i2c_dev->msg_complete);
 	}
@@ -669,17 +795,161 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	/* An error occurred, mask all interrupts */
 	tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
 		I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
-		I2C_INT_RX_FIFO_DATA_REQ);
+		I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_ALL_PACKETS_XFER_COMPLETE);
 	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
+	if (i2c_dev->is_curr_dma_xfer) {
+		dmaengine_terminate_all(chan);
+		complete(&i2c_dev->dma_complete);
+	}
+
 	complete(&i2c_dev->msg_complete);
 done:
 	spin_unlock(&i2c_dev->xfer_lock);
 	return IRQ_HANDLED;
 }
 
+static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
+				       size_t len, int direction)
+{
+	u32 val, reg;
+	u8 dma_burst = 0;
+	struct dma_slave_config dma_sconfig;
+
+	if (i2c_dev->hw->has_mst_fifo)
+		reg = I2C_MST_FIFO_CONTROL;
+	else
+		reg = I2C_FIFO_CONTROL;
+	val = i2c_readl(i2c_dev, reg);
+
+	if (len & 0xF)
+		dma_burst = 1;
+	else if (len & 0x10)
+		dma_burst = 4;
+	else
+		dma_burst = 8;
+
+	if (direction == DATA_DMA_DIR_TX) {
+		if (i2c_dev->hw->has_mst_fifo)
+			val |= I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
+		else
+			val |= I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
+	} else {
+		if (i2c_dev->hw->has_mst_fifo)
+			val |= I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
+		else
+			val |= I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
+	}
+	i2c_writel(i2c_dev, val, reg);
+
+	if (direction == DATA_DMA_DIR_TX) {
+		dma_sconfig.dst_addr = i2c_dev->phys_addr + I2C_TX_FIFO;
+		dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_sconfig.dst_maxburst = dma_burst;
+	} else {
+		dma_sconfig.src_addr = i2c_dev->phys_addr + I2C_RX_FIFO;
+		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_sconfig.src_maxburst = dma_burst;
+	}
+
+	dmaengine_slave_config(chan, &dma_sconfig);
+}
+
+static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
+{
+	u32 *buffer = (u32 *)i2c_dev->msg_buf;
+	unsigned long time_left, flags;
+	int ret = 0;
+	u32 int_mask;
+	size_t tx_len = 0;
+	size_t rx_len = 0;
+
+	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
+	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+	if (i2c_dev->msg_read) {
+		chan = i2c_dev->rx_dma_chan;
+		rx_len = ALIGN(i2c_dev->msg_buf_remaining,
+							BYTES_PER_FIFO_WORD);
+
+		tegra_i2c_config_fifo_trig(i2c_dev, rx_len, DATA_DMA_DIR_RX);
+		/* make the dma buffer to read by dma */
+		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
+					   i2c_dev->dma_buf_size,
+					   DMA_FROM_DEVICE);
+
+		ret = tegra_i2c_dma_submit(i2c_dev, rx_len);
+		if (ret < 0) {
+			dev_err(i2c_dev->dev,
+				"Starting RX DMA failed, err %d\n", ret);
+			goto exit;
+		}
+
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	} else {
+		chan = i2c_dev->tx_dma_chan;
+		tx_len = ALIGN(i2c_dev->msg_buf_remaining,
+				BYTES_PER_FIFO_WORD) + I2C_PACKET_HEADER_SIZE;
+
+		tegra_i2c_config_fifo_trig(i2c_dev, tx_len, DATA_DMA_DIR_TX);
+		/* Make the dma buffer to read by cpu */
+		dma_sync_single_for_cpu(i2c_dev->dev, i2c_dev->dma_phys,
+					i2c_dev->dma_buf_size, DMA_TO_DEVICE);
+
+		memcpy(i2c_dev->dma_buf, buffer, tx_len);
+		/* make the dma buffer to read by dma */
+		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
+					   i2c_dev->dma_buf_size,
+					   DMA_TO_DEVICE);
+
+		ret = tegra_i2c_dma_submit(i2c_dev, tx_len);
+		if (ret < 0) {
+			dev_err(i2c_dev->dev,
+				"Starting TX DMA failed, err %d\n", ret);
+			goto exit;
+		}
+	}
+
+	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
+		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
+	int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+exit:
+	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+	if (ret)
+		return ret;
+
+	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+		i2c_readl(i2c_dev, I2C_INT_MASK));
+
+	time_left = wait_for_completion_timeout(&i2c_dev->dma_complete,
+						TEGRA_I2C_TIMEOUT);
+	if (time_left == 0) {
+		dev_err(i2c_dev->dev, "DMA transfer timeout\n");
+		dmaengine_terminate_all(chan);
+		return -ETIMEDOUT;
+	}
+
+	if (i2c_dev->msg_read) {
+		if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) {
+			dma_sync_single_for_cpu(i2c_dev->dev,
+						i2c_dev->dma_phys,
+						i2c_dev->dma_buf_size,
+						DMA_FROM_DEVICE);
+
+			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, rx_len);
+		}
+	}
+
+	return 0;
+}
+
 static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
 {
 	u32 *buffer = (u32 *)i2c_dev->msg_buf;
@@ -695,7 +965,7 @@ static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
 	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
 	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
 
-	i2c_dev->msg_buf = (u8 *) buffer;
+	i2c_dev->msg_buf = (u8 *)buffer;
 
 	if (!i2c_dev->msg_read)
 		tegra_i2c_fill_tx_fifo(i2c_dev);
@@ -723,12 +993,25 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	unsigned long time_left;
 	u32 *buffer;
 	int ret = 0;
+	size_t xfer_size = 0;
+	bool dma = false;
 
 	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
-					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
+			 I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
 	if (!buffer)
 		return -ENOMEM;
 
+	if (msg->flags & I2C_M_RD)
+		xfer_size = msg->len;
+	else
+		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
+			    I2C_PACKET_HEADER_SIZE;
+
+	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
+		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);
+
+	i2c_dev->is_curr_dma_xfer = dma;
+
 	tegra_i2c_flush_fifos(i2c_dev);
 
 	i2c_dev->msg_buf = (u8 *)buffer;
@@ -764,12 +1047,24 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (!(msg->flags & I2C_M_RD))
 		memcpy(buffer, msg->buf, msg->len);
 
-	tegra_i2c_start_pio_xfer(i2c_dev);
+	if (dma)
+		ret = tegra_i2c_start_dma_xfer(i2c_dev);
+	else
+		ret = tegra_i2c_start_pio_xfer(i2c_dev);
+
+	if (ret == -EIO)
+		goto err_xfer;
+	else if (ret == -ETIMEDOUT)
+		goto end_xfer;
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
 						TEGRA_I2C_TIMEOUT);
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+		if (i2c_dev->is_curr_dma_xfer) {
+			dmaengine_terminate_all(chan);
+			complete(&i2c_dev->dma_complete);
+		}
 
 		tegra_i2c_init(i2c_dev);
 		ret = -ETIMEDOUT;
@@ -777,12 +1072,20 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	}
 
 	if (i2c_dev->msg_read) {
-		i2c_dev->msg_buf = (u8 *)buffer;
+		if (!dma)
+			i2c_dev->msg_buf = (u8 *)buffer;
 		memcpy(msg->buf, i2c_dev->msg_buf, msg->len);
 	}
 
+end_xfer:
 	int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
 	tegra_i2c_mask_irq(i2c_dev, int_mask);
+	if (i2c_dev->is_curr_dma_xfer && (ret < 0)) {
+		dev_err(i2c_dev->dev, "i2c DMA transfer failed\n");
+		tegra_i2c_init(i2c_dev);
+		ret = -ETIMEDOUT;
+		goto err_xfer;
+	}
 
 	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
 		time_left, completion_done(&i2c_dev->msg_complete),
@@ -819,6 +1122,19 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 		return ret;
 	}
 
+	if (i2c_dev->has_dma) {
+		if (!i2c_dev->rx_dma_chan) {
+			ret = tegra_i2c_init_dma_param(i2c_dev, true);
+			if (ret < 0)
+				return ret;
+		}
+		if (!i2c_dev->tx_dma_chan) {
+			ret = tegra_i2c_init_dma_param(i2c_dev, false);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
 	for (i = 0; i < num; i++) {
 		enum msg_end_type end_type = MSG_END_STOP;
 
@@ -861,6 +1177,8 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
 
 	i2c_dev->is_multimaster_mode = of_property_read_bool(np,
 			"multi-master");
+
+	i2c_dev->has_dma = of_property_read_bool(np, "dmas");
 }
 
 static const struct i2c_algorithm tegra_i2c_algo = {
@@ -973,11 +1291,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	struct clk *div_clk;
 	struct clk *fast_clk;
 	void __iomem *base;
+	phys_addr_t phys_addr;
 	int irq;
 	int ret = 0;
 	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	phys_addr = res->start;
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
@@ -1000,12 +1320,14 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	i2c_dev->base = base;
+	i2c_dev->phys_addr = phys_addr;
 	i2c_dev->div_clk = div_clk;
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
 	i2c_dev->adapter.quirks = &tegra_i2c_quirks;
 	i2c_dev->irq = irq;
 	i2c_dev->cont_id = pdev->id;
 	i2c_dev->dev = &pdev->dev;
+	i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len;
 
 	i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c");
 	if (IS_ERR(i2c_dev->rst)) {
@@ -1020,6 +1342,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 						  "nvidia,tegra20-i2c-dvc");
 	init_completion(&i2c_dev->msg_complete);
 	spin_lock_init(&i2c_dev->xfer_lock);
+	init_completion(&i2c_dev->dma_complete);
 
 	if (!i2c_dev->hw->has_single_clk_source) {
 		fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
@@ -1079,6 +1402,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (i2c_dev->has_dma) {
+		ret = tegra_i2c_init_dma_param(i2c_dev, true);
+		if (ret == -EPROBE_DEFER)
+			goto disable_div_clk;
+		ret = tegra_i2c_init_dma_param(i2c_dev, false);
+		if (ret == -EPROBE_DEFER)
+			goto disable_div_clk;
+	}
+
 	ret = tegra_i2c_init(i2c_dev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 3/4] i2c: tegra: Add DMA Support
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch adds DMA support for Tegra I2C.

Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
transfer size of the max FIFO depth and DMA mode is used for
transfer size higher than max FIFO depth to save CPU overhead.

PIO mode needs full intervention of CPU to fill or empty FIFO's
and also need to service multiple data requests interrupt for the
same transaction adding overhead on CPU for large transfers.

DMA mode is helpful for Large transfers during downloading or
uploading FW over I2C to some external devices.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Updated based on V1 review feedback along with code cleanup for
	proper implementation of DMA.

 drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 349 insertions(+), 17 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 13bce1411ddc..769700d5a7f3 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -9,6 +9,9 @@
 #include <asm/unaligned.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -46,6 +49,8 @@
 #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
 #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
 #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
+#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
+#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
 #define I2C_FIFO_STATUS				0x060
 #define I2C_FIFO_STATUS_TX_MASK			0xF0
 #define I2C_FIFO_STATUS_TX_SHIFT		4
@@ -120,6 +125,16 @@
 /* Packet header size in bytes */
 #define I2C_PACKET_HEADER_SIZE			12
 
+#define DATA_DMA_DIR_TX				(1 << 0)
+#define DATA_DMA_DIR_RX				(1 << 1)
+
+/*
+ * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
+ * above this, controller will use DMA to fill FIFO.
+ * MAX PIO len is 20 bytes excluding packet header.
+ */
+#define I2C_PIO_MODE_MAX_LEN			32
+
 /*
  * msg_end_type: The bus control which need to be send at end of transfer.
  * @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
  * @fast_clk: clock reference for fast clock of I2C controller
  * @rst: reset control for the I2C controller
  * @base: ioremapped registers cookie
+ * @phys_addr: Physical address of I2C base address to use for DMA configuration
  * @cont_id: I2C controller ID, used for packet header
  * @irq: IRQ number of transfer complete interrupt
  * @irq_disabled: used to track whether or not the interrupt is enabled
@@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
  * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
  * @is_multimaster_mode: track if I2C controller is in multi-master mode
  * @xfer_lock: lock to serialize transfer submission and processing
+ * @has_dma: indicated if controller supports DMA
+ * @tx_dma_chan: DMA transmit channel
+ * @rx_dma_chan: DMA receive channel
+ * @dma_phys: handle to DMA resources
+ * @dma_buf: pointer to allocated DMA buffer
+ * @dma_buf_size: DMA buffer size
+ * @is_curr_dma_xfer: indicates active DMA transfer
+ * @dma_complete: DMA completion notifier
  */
 struct tegra_i2c_dev {
 	struct device *dev;
@@ -202,6 +226,7 @@ struct tegra_i2c_dev {
 	struct clk *fast_clk;
 	struct reset_control *rst;
 	void __iomem *base;
+	phys_addr_t phys_addr;
 	int cont_id;
 	int irq;
 	bool irq_disabled;
@@ -215,8 +240,18 @@ struct tegra_i2c_dev {
 	u16 clk_divisor_non_hs_mode;
 	bool is_multimaster_mode;
 	spinlock_t xfer_lock;
+	bool has_dma;
+	struct dma_chan *tx_dma_chan;
+	struct dma_chan *rx_dma_chan;
+	dma_addr_t dma_phys;
+	u32 *dma_buf;
+	unsigned int dma_buf_size;
+	bool is_curr_dma_xfer;
+	struct completion dma_complete;
 };
 
+static struct dma_chan *chan;
+
 static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
 		       unsigned long reg)
 {
@@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
 	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
 }
 
+static void tegra_i2c_dma_complete(void *args)
+{
+	struct tegra_i2c_dev *i2c_dev = args;
+
+	complete(&i2c_dev->dma_complete);
+}
+
+static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
+{
+	struct dma_async_tx_descriptor *dma_desc;
+	enum dma_transfer_direction dir;
+
+	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
+	reinit_completion(&i2c_dev->dma_complete);
+	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
+					       len, dir, DMA_PREP_INTERRUPT |
+					       DMA_CTRL_ACK);
+	if (!dma_desc) {
+		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
+		return -EIO;
+	}
+
+	dma_desc->callback = tegra_i2c_dma_complete;
+	dma_desc->callback_param = i2c_dev;
+	dmaengine_submit(dma_desc);
+	dma_async_issue_pending(chan);
+	return 0;
+}
+
+static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
+				    bool dma_to_memory)
+{
+	struct dma_chan *dma_chan;
+	u32 *dma_buf;
+	dma_addr_t dma_phys;
+	int ret;
+	const char *chan_name = dma_to_memory ? "rx" : "tx";
+
+	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
+	if (IS_ERR(dma_chan))
+		return PTR_ERR(dma_chan);
+
+	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+				     &dma_phys, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(i2c_dev->dev, "Failed to allocate the DMA buffer\n");
+		ret = -ENOMEM;
+		goto scrub;
+	}
+
+	if (dma_to_memory)
+		i2c_dev->rx_dma_chan = dma_chan;
+	else
+		i2c_dev->tx_dma_chan = dma_chan;
+
+	i2c_dev->dma_buf = dma_buf;
+	i2c_dev->dma_phys = dma_phys;
+
+	return 0;
+
+scrub:
+	dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+			  dma_buf, dma_phys);
+	dma_release_channel(dma_chan);
+	return ret;
+}
+
 static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -529,6 +633,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 		return err;
 	}
 
+	i2c_dev->is_curr_dma_xfer = false;
+
 	reset_control_assert(i2c_dev->rst);
 	udelay(2);
 	reset_control_deassert(i2c_dev->rst);
@@ -642,25 +748,45 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 		goto err;
 	}
 
-	if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
-		if (i2c_dev->msg_buf_remaining)
-			tegra_i2c_empty_rx_fifo(i2c_dev);
-		else
-			BUG();
-	}
+	if (!i2c_dev->is_curr_dma_xfer) {
+		if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
+			if (i2c_dev->msg_buf_remaining)
+				tegra_i2c_empty_rx_fifo(i2c_dev);
+			else
+				BUG();
+		}
 
-	if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
-		if (i2c_dev->msg_buf_remaining)
-			tegra_i2c_fill_tx_fifo(i2c_dev);
-		else
-			tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
+		if (!i2c_dev->msg_read &&
+		   (status & I2C_INT_TX_FIFO_DATA_REQ)) {
+			if (i2c_dev->msg_buf_remaining)
+				tegra_i2c_fill_tx_fifo(i2c_dev);
+			else
+				tegra_i2c_mask_irq(i2c_dev,
+						   I2C_INT_TX_FIFO_DATA_REQ);
+		}
 	}
 
 	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
-	if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+	if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) {
+	/*
+	 * During message read XFER_COMPLETE interrupt is triggered prior to
+	 * DMA complete notification and during message write XFER_COMPLETE
+	 * interrupt is triggered after DMA complete notification.
+	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
+	 * so forcing msg_buf_remaining to 0.
+	 */
+		if (i2c_dev->is_curr_dma_xfer)
+			i2c_dev->msg_buf_remaining = 0;
+		status |= I2C_INT_PACKET_XFER_COMPLETE;
+		i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+		if (!i2c_dev->msg_buf_remaining)
+			complete(&i2c_dev->msg_complete);
+	} else if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+		if (i2c_dev->is_curr_dma_xfer)
+			i2c_dev->msg_buf_remaining = 0;
 		BUG_ON(i2c_dev->msg_buf_remaining);
 		complete(&i2c_dev->msg_complete);
 	}
@@ -669,17 +795,161 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	/* An error occurred, mask all interrupts */
 	tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
 		I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
-		I2C_INT_RX_FIFO_DATA_REQ);
+		I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_ALL_PACKETS_XFER_COMPLETE);
 	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
+	if (i2c_dev->is_curr_dma_xfer) {
+		dmaengine_terminate_all(chan);
+		complete(&i2c_dev->dma_complete);
+	}
+
 	complete(&i2c_dev->msg_complete);
 done:
 	spin_unlock(&i2c_dev->xfer_lock);
 	return IRQ_HANDLED;
 }
 
+static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
+				       size_t len, int direction)
+{
+	u32 val, reg;
+	u8 dma_burst = 0;
+	struct dma_slave_config dma_sconfig;
+
+	if (i2c_dev->hw->has_mst_fifo)
+		reg = I2C_MST_FIFO_CONTROL;
+	else
+		reg = I2C_FIFO_CONTROL;
+	val = i2c_readl(i2c_dev, reg);
+
+	if (len & 0xF)
+		dma_burst = 1;
+	else if (len & 0x10)
+		dma_burst = 4;
+	else
+		dma_burst = 8;
+
+	if (direction == DATA_DMA_DIR_TX) {
+		if (i2c_dev->hw->has_mst_fifo)
+			val |= I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
+		else
+			val |= I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
+	} else {
+		if (i2c_dev->hw->has_mst_fifo)
+			val |= I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
+		else
+			val |= I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
+	}
+	i2c_writel(i2c_dev, val, reg);
+
+	if (direction == DATA_DMA_DIR_TX) {
+		dma_sconfig.dst_addr = i2c_dev->phys_addr + I2C_TX_FIFO;
+		dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_sconfig.dst_maxburst = dma_burst;
+	} else {
+		dma_sconfig.src_addr = i2c_dev->phys_addr + I2C_RX_FIFO;
+		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_sconfig.src_maxburst = dma_burst;
+	}
+
+	dmaengine_slave_config(chan, &dma_sconfig);
+}
+
+static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
+{
+	u32 *buffer = (u32 *)i2c_dev->msg_buf;
+	unsigned long time_left, flags;
+	int ret = 0;
+	u32 int_mask;
+	size_t tx_len = 0;
+	size_t rx_len = 0;
+
+	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
+	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+	if (i2c_dev->msg_read) {
+		chan = i2c_dev->rx_dma_chan;
+		rx_len = ALIGN(i2c_dev->msg_buf_remaining,
+							BYTES_PER_FIFO_WORD);
+
+		tegra_i2c_config_fifo_trig(i2c_dev, rx_len, DATA_DMA_DIR_RX);
+		/* make the dma buffer to read by dma */
+		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
+					   i2c_dev->dma_buf_size,
+					   DMA_FROM_DEVICE);
+
+		ret = tegra_i2c_dma_submit(i2c_dev, rx_len);
+		if (ret < 0) {
+			dev_err(i2c_dev->dev,
+				"Starting RX DMA failed, err %d\n", ret);
+			goto exit;
+		}
+
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
+	} else {
+		chan = i2c_dev->tx_dma_chan;
+		tx_len = ALIGN(i2c_dev->msg_buf_remaining,
+				BYTES_PER_FIFO_WORD) + I2C_PACKET_HEADER_SIZE;
+
+		tegra_i2c_config_fifo_trig(i2c_dev, tx_len, DATA_DMA_DIR_TX);
+		/* Make the dma buffer to read by cpu */
+		dma_sync_single_for_cpu(i2c_dev->dev, i2c_dev->dma_phys,
+					i2c_dev->dma_buf_size, DMA_TO_DEVICE);
+
+		memcpy(i2c_dev->dma_buf, buffer, tx_len);
+		/* make the dma buffer to read by dma */
+		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
+					   i2c_dev->dma_buf_size,
+					   DMA_TO_DEVICE);
+
+		ret = tegra_i2c_dma_submit(i2c_dev, tx_len);
+		if (ret < 0) {
+			dev_err(i2c_dev->dev,
+				"Starting TX DMA failed, err %d\n", ret);
+			goto exit;
+		}
+	}
+
+	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
+		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
+	int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+	tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+exit:
+	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+	if (ret)
+		return ret;
+
+	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+		i2c_readl(i2c_dev, I2C_INT_MASK));
+
+	time_left = wait_for_completion_timeout(&i2c_dev->dma_complete,
+						TEGRA_I2C_TIMEOUT);
+	if (time_left == 0) {
+		dev_err(i2c_dev->dev, "DMA transfer timeout\n");
+		dmaengine_terminate_all(chan);
+		return -ETIMEDOUT;
+	}
+
+	if (i2c_dev->msg_read) {
+		if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) {
+			dma_sync_single_for_cpu(i2c_dev->dev,
+						i2c_dev->dma_phys,
+						i2c_dev->dma_buf_size,
+						DMA_FROM_DEVICE);
+
+			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, rx_len);
+		}
+	}
+
+	return 0;
+}
+
 static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
 {
 	u32 *buffer = (u32 *)i2c_dev->msg_buf;
@@ -695,7 +965,7 @@ static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
 	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
 	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
 
-	i2c_dev->msg_buf = (u8 *) buffer;
+	i2c_dev->msg_buf = (u8 *)buffer;
 
 	if (!i2c_dev->msg_read)
 		tegra_i2c_fill_tx_fifo(i2c_dev);
@@ -723,12 +993,25 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	unsigned long time_left;
 	u32 *buffer;
 	int ret = 0;
+	size_t xfer_size = 0;
+	bool dma = false;
 
 	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
-					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
+			 I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
 	if (!buffer)
 		return -ENOMEM;
 
+	if (msg->flags & I2C_M_RD)
+		xfer_size = msg->len;
+	else
+		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
+			    I2C_PACKET_HEADER_SIZE;
+
+	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
+		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);
+
+	i2c_dev->is_curr_dma_xfer = dma;
+
 	tegra_i2c_flush_fifos(i2c_dev);
 
 	i2c_dev->msg_buf = (u8 *)buffer;
@@ -764,12 +1047,24 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (!(msg->flags & I2C_M_RD))
 		memcpy(buffer, msg->buf, msg->len);
 
-	tegra_i2c_start_pio_xfer(i2c_dev);
+	if (dma)
+		ret = tegra_i2c_start_dma_xfer(i2c_dev);
+	else
+		ret = tegra_i2c_start_pio_xfer(i2c_dev);
+
+	if (ret == -EIO)
+		goto err_xfer;
+	else if (ret == -ETIMEDOUT)
+		goto end_xfer;
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
 						TEGRA_I2C_TIMEOUT);
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+		if (i2c_dev->is_curr_dma_xfer) {
+			dmaengine_terminate_all(chan);
+			complete(&i2c_dev->dma_complete);
+		}
 
 		tegra_i2c_init(i2c_dev);
 		ret = -ETIMEDOUT;
@@ -777,12 +1072,20 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	}
 
 	if (i2c_dev->msg_read) {
-		i2c_dev->msg_buf = (u8 *)buffer;
+		if (!dma)
+			i2c_dev->msg_buf = (u8 *)buffer;
 		memcpy(msg->buf, i2c_dev->msg_buf, msg->len);
 	}
 
+end_xfer:
 	int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
 	tegra_i2c_mask_irq(i2c_dev, int_mask);
+	if (i2c_dev->is_curr_dma_xfer && (ret < 0)) {
+		dev_err(i2c_dev->dev, "i2c DMA transfer failed\n");
+		tegra_i2c_init(i2c_dev);
+		ret = -ETIMEDOUT;
+		goto err_xfer;
+	}
 
 	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
 		time_left, completion_done(&i2c_dev->msg_complete),
@@ -819,6 +1122,19 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 		return ret;
 	}
 
+	if (i2c_dev->has_dma) {
+		if (!i2c_dev->rx_dma_chan) {
+			ret = tegra_i2c_init_dma_param(i2c_dev, true);
+			if (ret < 0)
+				return ret;
+		}
+		if (!i2c_dev->tx_dma_chan) {
+			ret = tegra_i2c_init_dma_param(i2c_dev, false);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
 	for (i = 0; i < num; i++) {
 		enum msg_end_type end_type = MSG_END_STOP;
 
@@ -861,6 +1177,8 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
 
 	i2c_dev->is_multimaster_mode = of_property_read_bool(np,
 			"multi-master");
+
+	i2c_dev->has_dma = of_property_read_bool(np, "dmas");
 }
 
 static const struct i2c_algorithm tegra_i2c_algo = {
@@ -973,11 +1291,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	struct clk *div_clk;
 	struct clk *fast_clk;
 	void __iomem *base;
+	phys_addr_t phys_addr;
 	int irq;
 	int ret = 0;
 	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	phys_addr = res->start;
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
@@ -1000,12 +1320,14 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	i2c_dev->base = base;
+	i2c_dev->phys_addr = phys_addr;
 	i2c_dev->div_clk = div_clk;
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
 	i2c_dev->adapter.quirks = &tegra_i2c_quirks;
 	i2c_dev->irq = irq;
 	i2c_dev->cont_id = pdev->id;
 	i2c_dev->dev = &pdev->dev;
+	i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len;
 
 	i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c");
 	if (IS_ERR(i2c_dev->rst)) {
@@ -1020,6 +1342,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 						  "nvidia,tegra20-i2c-dvc");
 	init_completion(&i2c_dev->msg_complete);
 	spin_lock_init(&i2c_dev->xfer_lock);
+	init_completion(&i2c_dev->dma_complete);
 
 	if (!i2c_dev->hw->has_single_clk_source) {
 		fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
@@ -1079,6 +1402,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (i2c_dev->has_dma) {
+		ret = tegra_i2c_init_dma_param(i2c_dev, true);
+		if (ret == -EPROBE_DEFER)
+			goto disable_div_clk;
+		ret = tegra_i2c_init_dma_param(i2c_dev, false);
+		if (ret == -EPROBE_DEFER)
+			goto disable_div_clk;
+	}
+
 	ret = tegra_i2c_init(i2c_dev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 4/4] i2c: tegra: Update transfer timeout
  2019-01-24 20:51 ` Sowjanya Komatineni
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  -1 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

Update I2C transfer timeout based on transfer bytes and I2C bus
rate to allow enough time during max transfer size based on the
speed.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Added this patch in V2 series to allow enough time for data transfer
	to happen.
	This patch has dependency with DMA patch as TEGRA_I2C_TIMEOUT define
	takes argument with this patch.

 drivers/i2c/busses/i2c-tegra.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 769700d5a7f3..2f8de837f660 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -27,7 +27,7 @@
 #include <linux/reset.h>
 #include <linux/slab.h>
 
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define TEGRA_I2C_TIMEOUT(ms) (msecs_to_jiffies(ms))
 #define BYTES_PER_FIFO_WORD 4
 
 #define I2C_CNFG				0x000
@@ -857,7 +857,8 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
 	dmaengine_slave_config(chan, &dma_sconfig);
 }
 
-static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev,
+				    u16 xfer_time)
 {
 	u32 *buffer = (u32 *)i2c_dev->msg_buf;
 	unsigned long time_left, flags;
@@ -929,7 +930,7 @@ static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
 		i2c_readl(i2c_dev, I2C_INT_MASK));
 
 	time_left = wait_for_completion_timeout(&i2c_dev->dma_complete,
-						TEGRA_I2C_TIMEOUT);
+						TEGRA_I2C_TIMEOUT(xfer_time));
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "DMA transfer timeout\n");
 		dmaengine_terminate_all(chan);
@@ -995,6 +996,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	int ret = 0;
 	size_t xfer_size = 0;
 	bool dma = false;
+	u16 xfer_time = 100;
 
 	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
 			 I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
@@ -1012,6 +1014,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 
 	i2c_dev->is_curr_dma_xfer = dma;
 
+	xfer_time += DIV_ROUND_CLOSEST((xfer_size * 9) * 1000,
+					i2c_dev->bus_clk_rate);
+
 	tegra_i2c_flush_fifos(i2c_dev);
 
 	i2c_dev->msg_buf = (u8 *)buffer;
@@ -1048,7 +1053,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		memcpy(buffer, msg->buf, msg->len);
 
 	if (dma)
-		ret = tegra_i2c_start_dma_xfer(i2c_dev);
+		ret = tegra_i2c_start_dma_xfer(i2c_dev, xfer_time);
 	else
 		ret = tegra_i2c_start_pio_xfer(i2c_dev);
 
@@ -1058,7 +1063,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		goto end_xfer;
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
-						TEGRA_I2C_TIMEOUT);
+						TEGRA_I2C_TIMEOUT(xfer_time));
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 		if (i2c_dev->is_curr_dma_xfer) {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH V2 4/4] i2c: tegra: Update transfer timeout
@ 2019-01-24 20:51   ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-24 20:51 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

Update I2C transfer timeout based on transfer bytes and I2C bus
rate to allow enough time during max transfer size based on the
speed.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V2] : Added this patch in V2 series to allow enough time for data transfer
	to happen.
	This patch has dependency with DMA patch as TEGRA_I2C_TIMEOUT define
	takes argument with this patch.

 drivers/i2c/busses/i2c-tegra.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 769700d5a7f3..2f8de837f660 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -27,7 +27,7 @@
 #include <linux/reset.h>
 #include <linux/slab.h>
 
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define TEGRA_I2C_TIMEOUT(ms) (msecs_to_jiffies(ms))
 #define BYTES_PER_FIFO_WORD 4
 
 #define I2C_CNFG				0x000
@@ -857,7 +857,8 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
 	dmaengine_slave_config(chan, &dma_sconfig);
 }
 
-static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev,
+				    u16 xfer_time)
 {
 	u32 *buffer = (u32 *)i2c_dev->msg_buf;
 	unsigned long time_left, flags;
@@ -929,7 +930,7 @@ static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
 		i2c_readl(i2c_dev, I2C_INT_MASK));
 
 	time_left = wait_for_completion_timeout(&i2c_dev->dma_complete,
-						TEGRA_I2C_TIMEOUT);
+						TEGRA_I2C_TIMEOUT(xfer_time));
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "DMA transfer timeout\n");
 		dmaengine_terminate_all(chan);
@@ -995,6 +996,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	int ret = 0;
 	size_t xfer_size = 0;
 	bool dma = false;
+	u16 xfer_time = 100;
 
 	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
 			 I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
@@ -1012,6 +1014,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 
 	i2c_dev->is_curr_dma_xfer = dma;
 
+	xfer_time += DIV_ROUND_CLOSEST((xfer_size * 9) * 1000,
+					i2c_dev->bus_clk_rate);
+
 	tegra_i2c_flush_fifos(i2c_dev);
 
 	i2c_dev->msg_buf = (u8 *)buffer;
@@ -1048,7 +1053,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		memcpy(buffer, msg->buf, msg->len);
 
 	if (dma)
-		ret = tegra_i2c_start_dma_xfer(i2c_dev);
+		ret = tegra_i2c_start_dma_xfer(i2c_dev, xfer_time);
 	else
 		ret = tegra_i2c_start_pio_xfer(i2c_dev);
 
@@ -1058,7 +1063,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		goto end_xfer;
 
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
-						TEGRA_I2C_TIMEOUT);
+						TEGRA_I2C_TIMEOUT(xfer_time));
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 		if (i2c_dev->is_curr_dma_xfer) {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 1/4] i2c: tegra: Sort all the include headers alphabetically
  2019-01-24 20:51 ` Sowjanya Komatineni
                   ` (3 preceding siblings ...)
  (?)
@ 2019-01-24 21:31 ` Dmitry Osipenko
  -1 siblings, 0 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-24 21:31 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch sorts all the include headers alphabetically for the
> I2C tegra driver
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : Added this in V2 to sort the headers in tegra I2C
> 
>  drivers/i2c/busses/i2c-tegra.c | 21 ++++++++++-----------
>  1 file changed, 10 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index e417ebf7628c..ef854be4c837 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -6,24 +6,23 @@
>   * Author: Colin Cross <ccross@android.com>
>   */
>  
> -#include <linux/kernel.h>
> -#include <linux/init.h>
> -#include <linux/platform_device.h>
> +#include <asm/unaligned.h>

What about to clean up the includes as well? Seems <asm/unaligned.h> is not needed by the code.

>  #include <linux/clk.h>
> +#include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/i2c.h>
> -#include <linux/io.h>
> +#include <linux/init.h>
>  #include <linux/interrupt.h>
> -#include <linux/delay.h>
> -#include <linux/slab.h>
> -#include <linux/of_device.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
>  #include <linux/module.h>
> -#include <linux/reset.h>
> +#include <linux/of_device.h>
>  #include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
> -#include <linux/iopoll.h>
> -
> -#include <asm/unaligned.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
>  
>  #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
>  #define BYTES_PER_FIFO_WORD 4
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-24 20:51   ` Sowjanya Komatineni
  (?)
@ 2019-01-25 19:34   ` Dmitry Osipenko
  2019-01-25 20:31     ` Sowjanya Komatineni
  -1 siblings, 1 reply; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 19:34 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch adds DMA support for Tegra I2C.
> 
> Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
> transfer size of the max FIFO depth and DMA mode is used for
> transfer size higher than max FIFO depth to save CPU overhead.
> 
> PIO mode needs full intervention of CPU to fill or empty FIFO's
> and also need to service multiple data requests interrupt for the
> same transaction adding overhead on CPU for large transfers.
> 
> DMA mode is helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 349 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 13bce1411ddc..769700d5a7f3 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -9,6 +9,9 @@
>  #include <asm/unaligned.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dmapool.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/err.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
> @@ -46,6 +49,8 @@
>  #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
>  #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
>  #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
> +#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
> +#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
>  #define I2C_FIFO_STATUS				0x060
>  #define I2C_FIFO_STATUS_TX_MASK			0xF0
>  #define I2C_FIFO_STATUS_TX_SHIFT		4
> @@ -120,6 +125,16 @@
>  /* Packet header size in bytes */
>  #define I2C_PACKET_HEADER_SIZE			12
>  
> +#define DATA_DMA_DIR_TX				(1 << 0)
> +#define DATA_DMA_DIR_RX				(1 << 1)
> +
> +/*
> + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
> + * above this, controller will use DMA to fill FIFO.
> + * MAX PIO len is 20 bytes excluding packet header.
> + */
> +#define I2C_PIO_MODE_MAX_LEN			32
> +
>  /*
>   * msg_end_type: The bus control which need to be send at end of transfer.
>   * @MSG_END_STOP: Send stop pulse at end of transfer.
> @@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
>   * @fast_clk: clock reference for fast clock of I2C controller
>   * @rst: reset control for the I2C controller
>   * @base: ioremapped registers cookie
> + * @phys_addr: Physical address of I2C base address to use for DMA configuration
>   * @cont_id: I2C controller ID, used for packet header
>   * @irq: IRQ number of transfer complete interrupt
>   * @irq_disabled: used to track whether or not the interrupt is enabled
> @@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
>   * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
>   * @is_multimaster_mode: track if I2C controller is in multi-master mode
>   * @xfer_lock: lock to serialize transfer submission and processing
> + * @has_dma: indicated if controller supports DMA
> + * @tx_dma_chan: DMA transmit channel
> + * @rx_dma_chan: DMA receive channel
> + * @dma_phys: handle to DMA resources
> + * @dma_buf: pointer to allocated DMA buffer
> + * @dma_buf_size: DMA buffer size
> + * @is_curr_dma_xfer: indicates active DMA transfer
> + * @dma_complete: DMA completion notifier
>   */
>  struct tegra_i2c_dev {
>  	struct device *dev;
> @@ -202,6 +226,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t phys_addr;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -215,8 +240,18 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	bool has_dma;
> +	struct dma_chan *tx_dma_chan;
> +	struct dma_chan *rx_dma_chan;
> +	dma_addr_t dma_phys;
> +	u32 *dma_buf;
> +	unsigned int dma_buf_size;
> +	bool is_curr_dma_xfer;
> +	struct completion dma_complete;
>  };
>  
> +static struct dma_chan *chan;
> +
>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>  		       unsigned long reg)
>  {
> @@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>  	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
>  }
>  
> +static void tegra_i2c_dma_complete(void *args)
> +{
> +	struct tegra_i2c_dev *i2c_dev = args;
> +
> +	complete(&i2c_dev->dma_complete);
> +}
> +
> +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
> +{
> +	struct dma_async_tx_descriptor *dma_desc;
> +	enum dma_transfer_direction dir;
> +
> +	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
> +	reinit_completion(&i2c_dev->dma_complete);
> +	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> +	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
> +					       len, dir, DMA_PREP_INTERRUPT |
> +					       DMA_CTRL_ACK);
> +	if (!dma_desc) {
> +		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
> +		return -EIO;
> +	}
> +
> +	dma_desc->callback = tegra_i2c_dma_complete;
> +	dma_desc->callback_param = i2c_dev;
> +	dmaengine_submit(dma_desc);
> +	dma_async_issue_pending(chan);
> +	return 0;
> +}
> +
> +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
> +				    bool dma_to_memory)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int ret;
> +	const char *chan_name = dma_to_memory ? "rx" : "tx";
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);
> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +				     &dma_phys, GFP_KERNEL);
> +
> +	if (!dma_buf) {
> +		dev_err(i2c_dev->dev, "Failed to allocate the DMA buffer\n");
> +		ret = -ENOMEM;
> +		goto scrub;
> +	}
> +
> +	if (dma_to_memory)
> +		i2c_dev->rx_dma_chan = dma_chan;
> +	else
> +		i2c_dev->tx_dma_chan = dma_chan;
> +
> +	i2c_dev->dma_buf = dma_buf;
> +	i2c_dev->dma_phys = dma_phys;
> +
> +	return 0;
> +
> +scrub:
> +	dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +			  dma_buf, dma_phys);
> +	dma_release_channel(dma_chan);
> +	return ret;
> +}
> +
>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>  {
>  	unsigned long timeout = jiffies + HZ;
> @@ -529,6 +633,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  		return err;
>  	}
>  
> +	i2c_dev->is_curr_dma_xfer = false;
> +
>  	reset_control_assert(i2c_dev->rst);
>  	udelay(2);
>  	reset_control_deassert(i2c_dev->rst);
> @@ -642,25 +748,45 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  		goto err;
>  	}
>  
> -	if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
> -		if (i2c_dev->msg_buf_remaining)
> -			tegra_i2c_empty_rx_fifo(i2c_dev);
> -		else
> -			BUG();
> -	}
> +	if (!i2c_dev->is_curr_dma_xfer) {
> +		if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
> +			if (i2c_dev->msg_buf_remaining)
> +				tegra_i2c_empty_rx_fifo(i2c_dev);
> +			else
> +				BUG();
> +		}
>  
> -	if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
> -		if (i2c_dev->msg_buf_remaining)
> -			tegra_i2c_fill_tx_fifo(i2c_dev);
> -		else
> -			tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
> +		if (!i2c_dev->msg_read &&
> +		   (status & I2C_INT_TX_FIFO_DATA_REQ)) {
> +			if (i2c_dev->msg_buf_remaining)
> +				tegra_i2c_fill_tx_fifo(i2c_dev);
> +			else
> +				tegra_i2c_mask_irq(i2c_dev,
> +						   I2C_INT_TX_FIFO_DATA_REQ);
> +		}
>  	}
>  
>  	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> -	if (status & I2C_INT_PACKET_XFER_COMPLETE) {
> +	if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) {
> +	/*
> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
> +	 * DMA complete notification and during message write XFER_COMPLETE
> +	 * interrupt is triggered after DMA complete notification.
> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
> +	 * so forcing msg_buf_remaining to 0.
> +	 */
> +		if (i2c_dev->is_curr_dma_xfer)
> +			i2c_dev->msg_buf_remaining = 0;
> +		status |= I2C_INT_PACKET_XFER_COMPLETE;
> +		i2c_writel(i2c_dev, status, I2C_INT_STATUS);
> +		if (!i2c_dev->msg_buf_remaining)
> +			complete(&i2c_dev->msg_complete);
> +	} else if (status & I2C_INT_PACKET_XFER_COMPLETE) {
> +		if (i2c_dev->is_curr_dma_xfer)
> +			i2c_dev->msg_buf_remaining = 0;
>  		BUG_ON(i2c_dev->msg_buf_remaining);
>  		complete(&i2c_dev->msg_complete);
>  	}
> @@ -669,17 +795,161 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	/* An error occurred, mask all interrupts */
>  	tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
>  		I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
> -		I2C_INT_RX_FIFO_DATA_REQ);
> +		I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_ALL_PACKETS_XFER_COMPLETE);
>  	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> +	if (i2c_dev->is_curr_dma_xfer) {
> +		dmaengine_terminate_all(chan);
> +		complete(&i2c_dev->dma_complete);
> +	}
> +
>  	complete(&i2c_dev->msg_complete);
>  done:
>  	spin_unlock(&i2c_dev->xfer_lock);
>  	return IRQ_HANDLED;
>  }
>  
> +static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
> +				       size_t len, int direction)
> +{
> +	u32 val, reg;
> +	u8 dma_burst = 0;
> +	struct dma_slave_config dma_sconfig;
> +
> +	if (i2c_dev->hw->has_mst_fifo)
> +		reg = I2C_MST_FIFO_CONTROL;
> +	else
> +		reg = I2C_FIFO_CONTROL;
> +	val = i2c_readl(i2c_dev, reg);
> +
> +	if (len & 0xF)
> +		dma_burst = 1;
> +	else if (len & 0x10)
> +		dma_burst = 4;
> +	else
> +		dma_burst = 8;
> +
> +	if (direction == DATA_DMA_DIR_TX) {
> +		if (i2c_dev->hw->has_mst_fifo)
> +			val |= I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
> +		else
> +			val |= I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
> +	} else {
> +		if (i2c_dev->hw->has_mst_fifo)
> +			val |= I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
> +		else
> +			val |= I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
> +	}
> +	i2c_writel(i2c_dev, val, reg);
> +
> +	if (direction == DATA_DMA_DIR_TX) {
> +		dma_sconfig.dst_addr = i2c_dev->phys_addr + I2C_TX_FIFO;
> +		dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_sconfig.dst_maxburst = dma_burst;
> +	} else {
> +		dma_sconfig.src_addr = i2c_dev->phys_addr + I2C_RX_FIFO;
> +		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_sconfig.src_maxburst = dma_burst;
> +	}
> +
> +	dmaengine_slave_config(chan, &dma_sconfig);
> +}
> +
> +static int tegra_i2c_start_dma_xfer(struct tegra_i2c_dev *i2c_dev)
> +{
> +	u32 *buffer = (u32 *)i2c_dev->msg_buf;
> +	unsigned long time_left, flags;
> +	int ret = 0;
> +	u32 int_mask;
> +	size_t tx_len = 0;
> +	size_t rx_len = 0;
> +
> +	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
> +	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> +
> +	if (i2c_dev->msg_read) {
> +		chan = i2c_dev->rx_dma_chan;
> +		rx_len = ALIGN(i2c_dev->msg_buf_remaining,
> +							BYTES_PER_FIFO_WORD);
> +
> +		tegra_i2c_config_fifo_trig(i2c_dev, rx_len, DATA_DMA_DIR_RX);
> +		/* make the dma buffer to read by dma */
> +		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
> +					   i2c_dev->dma_buf_size,
> +					   DMA_FROM_DEVICE);
> +
> +		ret = tegra_i2c_dma_submit(i2c_dev, rx_len);
> +		if (ret < 0) {
> +			dev_err(i2c_dev->dev,
> +				"Starting RX DMA failed, err %d\n", ret);
> +			goto exit;
> +		}
> +
> +		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +		i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +	} else {
> +		chan = i2c_dev->tx_dma_chan;
> +		tx_len = ALIGN(i2c_dev->msg_buf_remaining,
> +				BYTES_PER_FIFO_WORD) + I2C_PACKET_HEADER_SIZE;
> +
> +		tegra_i2c_config_fifo_trig(i2c_dev, tx_len, DATA_DMA_DIR_TX);
> +		/* Make the dma buffer to read by cpu */
> +		dma_sync_single_for_cpu(i2c_dev->dev, i2c_dev->dma_phys,
> +					i2c_dev->dma_buf_size, DMA_TO_DEVICE);
> +
> +		memcpy(i2c_dev->dma_buf, buffer, tx_len);
> +		/* make the dma buffer to read by dma */
> +		dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys,
> +					   i2c_dev->dma_buf_size,
> +					   DMA_TO_DEVICE);
> +
> +		ret = tegra_i2c_dma_submit(i2c_dev, tx_len);
> +		if (ret < 0) {
> +			dev_err(i2c_dev->dev,
> +				"Starting TX DMA failed, err %d\n", ret);
> +			goto exit;
> +		}
> +	}
> +
> +	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
> +		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
> +	int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> +
> +exit:
> +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
> +		i2c_readl(i2c_dev, I2C_INT_MASK));
> +
> +	time_left = wait_for_completion_timeout(&i2c_dev->dma_complete,
> +						TEGRA_I2C_TIMEOUT);
> +	if (time_left == 0) {
> +		dev_err(i2c_dev->dev, "DMA transfer timeout\n");
> +		dmaengine_terminate_all(chan);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (i2c_dev->msg_read) {
> +		if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) {
> +			dma_sync_single_for_cpu(i2c_dev->dev,
> +						i2c_dev->dma_phys,
> +						i2c_dev->dma_buf_size,
> +						DMA_FROM_DEVICE);
> +
> +			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, rx_len);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
>  {
>  	u32 *buffer = (u32 *)i2c_dev->msg_buf;
> @@ -695,7 +965,7 @@ static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
>  	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>  	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>  
> -	i2c_dev->msg_buf = (u8 *) buffer;
> +	i2c_dev->msg_buf = (u8 *)buffer;
>  
>  	if (!i2c_dev->msg_read)
>  		tegra_i2c_fill_tx_fifo(i2c_dev);
> @@ -723,12 +993,25 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>  	unsigned long time_left;
>  	u32 *buffer;
>  	int ret = 0;
> +	size_t xfer_size = 0;
> +	bool dma = false;
>  
>  	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> -					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> +			 I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
>  	if (!buffer)
>  		return -ENOMEM;
>  
> +	if (msg->flags & I2C_M_RD)
> +		xfer_size = msg->len;
> +	else
> +		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> +			    I2C_PACKET_HEADER_SIZE;
> +
> +	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
> +		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);
> +
> +	i2c_dev->is_curr_dma_xfer = dma;
> +
>  	tegra_i2c_flush_fifos(i2c_dev);
>  
>  	i2c_dev->msg_buf = (u8 *)buffer;
> @@ -764,12 +1047,24 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>  	if (!(msg->flags & I2C_M_RD))
>  		memcpy(buffer, msg->buf, msg->len);
>  
> -	tegra_i2c_start_pio_xfer(i2c_dev);
> +	if (dma)
> +		ret = tegra_i2c_start_dma_xfer(i2c_dev);
> +	else
> +		ret = tegra_i2c_start_pio_xfer(i2c_dev);
> +
> +	if (ret == -EIO)
> +		goto err_xfer;
> +	else if (ret == -ETIMEDOUT)
> +		goto end_xfer;
>  
>  	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
>  						TEGRA_I2C_TIMEOUT);
>  	if (time_left == 0) {
>  		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
> +		if (i2c_dev->is_curr_dma_xfer) {
> +			dmaengine_terminate_all(chan);
> +			complete(&i2c_dev->dma_complete);
> +		}
>  
>  		tegra_i2c_init(i2c_dev);
>  		ret = -ETIMEDOUT;
> @@ -777,12 +1072,20 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>  	}
>  
>  	if (i2c_dev->msg_read) {
> -		i2c_dev->msg_buf = (u8 *)buffer;
> +		if (!dma)
> +			i2c_dev->msg_buf = (u8 *)buffer;
>  		memcpy(msg->buf, i2c_dev->msg_buf, msg->len);
>  	}
>  
> +end_xfer:
>  	int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
>  	tegra_i2c_mask_irq(i2c_dev, int_mask);
> +	if (i2c_dev->is_curr_dma_xfer && (ret < 0)) {
> +		dev_err(i2c_dev->dev, "i2c DMA transfer failed\n");
> +		tegra_i2c_init(i2c_dev);
> +		ret = -ETIMEDOUT;
> +		goto err_xfer;
> +	}
>  
>  	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
>  		time_left, completion_done(&i2c_dev->msg_complete),
> @@ -819,6 +1122,19 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
>  		return ret;
>  	}
>  
> +	if (i2c_dev->has_dma) {
> +		if (!i2c_dev->rx_dma_chan) {
> +			ret = tegra_i2c_init_dma_param(i2c_dev, true);
> +			if (ret < 0)
> +				return ret;
> +		}
> +		if (!i2c_dev->tx_dma_chan) {
> +			ret = tegra_i2c_init_dma_param(i2c_dev, false);
> +			if (ret < 0)
> +				return ret;
> +		}
> +	}
> +
>  	for (i = 0; i < num; i++) {
>  		enum msg_end_type end_type = MSG_END_STOP;
>  
> @@ -861,6 +1177,8 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
>  
>  	i2c_dev->is_multimaster_mode = of_property_read_bool(np,
>  			"multi-master");
> +
> +	i2c_dev->has_dma = of_property_read_bool(np, "dmas");
>  }
>  
>  static const struct i2c_algorithm tegra_i2c_algo = {
> @@ -973,11 +1291,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  	struct clk *div_clk;
>  	struct clk *fast_clk;
>  	void __iomem *base;
> +	phys_addr_t phys_addr;
>  	int irq;
>  	int ret = 0;
>  	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
>  
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	phys_addr = res->start;
>  	base = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(base))
>  		return PTR_ERR(base);
> @@ -1000,12 +1320,14 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	i2c_dev->base = base;
> +	i2c_dev->phys_addr = phys_addr;
>  	i2c_dev->div_clk = div_clk;
>  	i2c_dev->adapter.algo = &tegra_i2c_algo;
>  	i2c_dev->adapter.quirks = &tegra_i2c_quirks;
>  	i2c_dev->irq = irq;
>  	i2c_dev->cont_id = pdev->id;
>  	i2c_dev->dev = &pdev->dev;
> +	i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len;
>  
>  	i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c");
>  	if (IS_ERR(i2c_dev->rst)) {
> @@ -1020,6 +1342,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  						  "nvidia,tegra20-i2c-dvc");
>  	init_completion(&i2c_dev->msg_complete);
>  	spin_lock_init(&i2c_dev->xfer_lock);
> +	init_completion(&i2c_dev->dma_complete);
>  
>  	if (!i2c_dev->hw->has_single_clk_source) {
>  		fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
> @@ -1079,6 +1402,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> +	if (i2c_dev->has_dma) {
> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> +		if (ret == -EPROBE_DEFER)
> +			goto disable_div_clk;
> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> +		if (ret == -EPROBE_DEFER)
> +			goto disable_div_clk;

So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.

> +	}
> +
>  	ret = tegra_i2c_init(i2c_dev);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-24 20:51   ` Sowjanya Komatineni
  (?)
@ 2019-01-25 19:55   ` Dmitry Osipenko
  2019-01-25 20:20     ` Sowjanya Komatineni
  -1 siblings, 1 reply; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 19:55 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch prepares the buffer with the message bytes to be
> transmitted along with the packet header information and then
> performs i2c transfer in PIO mode.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : DMA support changes include preparing buffer with message bytes and
> 	and header before sending them through DMA. So splitted the whole
> 	change into 2 seperate patches in this series.
> 
>  drivers/i2c/busses/i2c-tegra.c | 97 +++++++++++++++++++++++++++++-------------
>  1 file changed, 68 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index ef854be4c837..13bce1411ddc 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -117,6 +117,9 @@
>  #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
>  #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
>  
> +/* Packet header size in bytes */
> +#define I2C_PACKET_HEADER_SIZE			12
> +
>  /*
>   * msg_end_type: The bus control which need to be send at end of transfer.
>   * @MSG_END_STOP: Send stop pulse at end of transfer.
> @@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> +static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev)
> +{
> +	u32 *buffer = (u32 *)i2c_dev->msg_buf;
> +	unsigned long flags;
> +	u32 int_mask;
> +
> +	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
> +
> +	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> +
> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> +
> +	i2c_dev->msg_buf = (u8 *) buffer;
> +
> +	if (!i2c_dev->msg_read)
> +		tegra_i2c_fill_tx_fifo(i2c_dev);
> +
> +	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
> +		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
> +	if (i2c_dev->msg_read)
> +		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
> +	else if (i2c_dev->msg_buf_remaining)
> +		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
> +
> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
> +	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
> +		i2c_readl(i2c_dev, I2C_INT_MASK));
> +
> +	return 0;
> +}
> +
>  static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>  	struct i2c_msg *msg, enum msg_end_type end_state)
>  {
>  	u32 packet_header;
>  	u32 int_mask;
>  	unsigned long time_left;
> -	unsigned long flags;
> +	u32 *buffer;
> +	int ret = 0;
> +
> +	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> +					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> +	if (!buffer)
> +		return -ENOMEM;

Isn't it possible to avoid "buffer" allocation / copying overhead and keep code as-is for the PIO mode?

[snip]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-25 19:55   ` Dmitry Osipenko
@ 2019-01-25 20:20     ` Sowjanya Komatineni
  2019-01-25 21:25       ` Dmitry Osipenko
  0 siblings, 1 reply; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 20:20 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c



> > This patch prepares the buffer with the message bytes to be 
> > transmitted along with the packet header information and then performs 
> > i2c transfer in PIO mode.
> > 
> > Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> > ---
> >  [V2] : DMA support changes include preparing buffer with message bytes and
> > 	and header before sending them through DMA. So splitted the whole
> > 	change into 2 seperate patches in this series.
> > 
> >  drivers/i2c/busses/i2c-tegra.c | 97 
> > +++++++++++++++++++++++++++++-------------
> >  1 file changed, 68 insertions(+), 29 deletions(-)
> > 
> > diff --git a/drivers/i2c/busses/i2c-tegra.c 
> > b/drivers/i2c/busses/i2c-tegra.c index ef854be4c837..13bce1411ddc 
> > 100644
> > --- a/drivers/i2c/busses/i2c-tegra.c
> > +++ b/drivers/i2c/busses/i2c-tegra.c
> > @@ -117,6 +117,9 @@
> >  #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
> >  #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
> >  
> > +/* Packet header size in bytes */
> > +#define I2C_PACKET_HEADER_SIZE			12
> > +
> >  /*
> >   * msg_end_type: The bus control which need to be send at end of transfer.
> >   * @MSG_END_STOP: Send stop pulse at end of transfer.
> > @@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
> >  	return IRQ_HANDLED;
> >  }
> >  
> > +static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev) {
> > +	u32 *buffer = (u32 *)i2c_dev->msg_buf;
> > +	unsigned long flags;
> > +	u32 int_mask;
> > +
> > +	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
> > +
> > +	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
> > +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> > +
> > +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> > +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> > +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
> > +
> > +	i2c_dev->msg_buf = (u8 *) buffer;
> > +
> > +	if (!i2c_dev->msg_read)
> > +		tegra_i2c_fill_tx_fifo(i2c_dev);
> > +
> > +	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
> > +		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
> > +	if (i2c_dev->msg_read)
> > +		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
> > +	else if (i2c_dev->msg_buf_remaining)
> > +		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
> > +
> > +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
> > +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
> > +	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
> > +		i2c_readl(i2c_dev, I2C_INT_MASK));
> > +
> > +	return 0;
> > +}
> > +
> >  static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
> >  	struct i2c_msg *msg, enum msg_end_type end_state)  {
> >  	u32 packet_header;
> >  	u32 int_mask;
> >  	unsigned long time_left;
> > -	unsigned long flags;
> > +	u32 *buffer;
> > +	int ret = 0;
> > +
> > +	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> > +					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> > +	if (!buffer)
> > +		return -ENOMEM;
>
> Isn't it possible to avoid "buffer" allocation / copying overhead and keep code as-is for the PIO mode?
>

Keeping PIO mode code as is and adding DMA mode will have redundant code for header generation and msg complete timeout sections.
Also in existing PIO mode implementation, TX FIFO is filled as soon as the word is ready but for DMA mode need to put all msg bytes along with header together to send thru DMA.
So created common buffer to perform PIO/DMA to reduce redundancy.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 19:34   ` Dmitry Osipenko
@ 2019-01-25 20:31     ` Sowjanya Komatineni
  2019-01-25 20:40       ` Dmitry Osipenko
  0 siblings, 1 reply; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 20:31 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c


> > +	if (i2c_dev->has_dma) {
> > +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> > +		if (ret == -EPROBE_DEFER)
> > +			goto disable_div_clk;
> > +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> > +		if (ret == -EPROBE_DEFER)
> > +			goto disable_div_clk;
>
> So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.

If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not available by the time tegra_init_dma_param tries to request slave channel and in those cases dma_request_slave_channel_reason will return EPROBE_DEFER for tegra I2C probe to retry

In case if DMA is available but DMA buffer allocation fails, then tegra_i2c_init_dma_param returns ENOMEM and probe also fails returning same ENOMEM

Thanks
Sowjanya


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 20:31     ` Sowjanya Komatineni
@ 2019-01-25 20:40       ` Dmitry Osipenko
  2019-01-25 21:11         ` Sowjanya Komatineni
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 20:40 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

25.01.2019 23:31, Sowjanya Komatineni пишет:
> 
>>> +	if (i2c_dev->has_dma) {
>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
>>> +		if (ret == -EPROBE_DEFER)
>>> +			goto disable_div_clk;
>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
>>> +		if (ret == -EPROBE_DEFER)
>>> +			goto disable_div_clk;
>>
>> So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.
> 
> If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not available by the time tegra_init_dma_param tries to request slave channel and in those cases dma_request_slave_channel_reason will return EPROBE_DEFER for tegra I2C probe to retry
> 
> In case if DMA is available but DMA buffer allocation fails, then tegra_i2c_init_dma_param returns ENOMEM and probe also fails returning same ENOMEM

Is that what you're going to change in the next version? Your current variant of the code doesn't fail the probe on ENOMEM and there is duplicated attempt to invoke tegra_i2c_init_dma_param() during the transfer.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 20:40       ` Dmitry Osipenko
@ 2019-01-25 21:11         ` Sowjanya Komatineni
  2019-01-25 21:49           ` Dmitry Osipenko
  2019-01-25 23:10           ` Thierry Reding
  0 siblings, 2 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 21:11 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c



> >>> +	if (i2c_dev->has_dma) {
> >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> >>> +		if (ret == -EPROBE_DEFER)
> >>> +			goto disable_div_clk;
> >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> >>> +		if (ret == -EPROBE_DEFER)
> >>> +			goto disable_div_clk;
> >>
> >> So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.
> > 
> > If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not available by the time tegra_init_dma_param tries to request slave channel and in those cases dma_request_slave_channel_reason will return EPROBE_DEFER for tegra I2C probe to retry
> > 
> > In case if DMA is available but DMA buffer allocation fails, then tegra_i2c_init_dma_param returns ENOMEM and probe also fails returning same ENOMEM
>
> Is that what you're going to change in the next version? Your current variant of the code doesn't fail the probe on ENOMEM and there is duplicated attempt to invoke tegra_i2c_init_dma_param() during the transfer.

Sorry correction to my previous reply.  If DMA buffer allocation fails, tegra_i2c_init_dma_param returns ENOMEM but probe will succeed as i2c transaction need to happen during  boot for some platform device programming for successful boot and they use PIO mode as xfer bytes is less and deferring i2c probe for ENOMEM causes boot to fail so during probe EPROBE_DEFER is only taken care.

Re-attempt of tegra_i2c_init_dma_param in xfer happens only if no successful DMA channel allocation happens prior to that ( during probe in case of ENOMEM).
DMA mode is mainly for large transfer, and i2c xfer returning failure due to failing DMA buffer allocation causes boot to hang as platform device programming need to happen which doesn’t need to use DMA.
Will fix this and will send updated patch to reattempt DMA request and buffer allocation during DMA mode transfer and will return fail for DMA mode I2C transfer...

Thanks
Sowjanya

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-25 20:20     ` Sowjanya Komatineni
@ 2019-01-25 21:25       ` Dmitry Osipenko
  2019-01-25 23:22         ` Dmitry Osipenko
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 21:25 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

25.01.2019 23:20, Sowjanya Komatineni пишет:
> 
> 
>>> This patch prepares the buffer with the message bytes to be 
>>> transmitted along with the packet header information and then performs 
>>> i2c transfer in PIO mode.
>>>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>  [V2] : DMA support changes include preparing buffer with message bytes and
>>> 	and header before sending them through DMA. So splitted the whole
>>> 	change into 2 seperate patches in this series.
>>>
>>>  drivers/i2c/busses/i2c-tegra.c | 97 
>>> +++++++++++++++++++++++++++++-------------
>>>  1 file changed, 68 insertions(+), 29 deletions(-)
>>>
>>> diff --git a/drivers/i2c/busses/i2c-tegra.c 
>>> b/drivers/i2c/busses/i2c-tegra.c index ef854be4c837..13bce1411ddc 
>>> 100644
>>> --- a/drivers/i2c/busses/i2c-tegra.c
>>> +++ b/drivers/i2c/busses/i2c-tegra.c
>>> @@ -117,6 +117,9 @@
>>>  #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
>>>  #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
>>>  
>>> +/* Packet header size in bytes */
>>> +#define I2C_PACKET_HEADER_SIZE			12
>>> +
>>>  /*
>>>   * msg_end_type: The bus control which need to be send at end of transfer.
>>>   * @MSG_END_STOP: Send stop pulse at end of transfer.
>>> @@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>>  	return IRQ_HANDLED;
>>>  }
>>>  
>>> +static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev) {
>>> +	u32 *buffer = (u32 *)i2c_dev->msg_buf;
>>> +	unsigned long flags;
>>> +	u32 int_mask;
>>> +
>>> +	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
>>> +
>>> +	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
>>> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
>>> +
>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>> +
>>> +	i2c_dev->msg_buf = (u8 *) buffer;
>>> +
>>> +	if (!i2c_dev->msg_read)
>>> +		tegra_i2c_fill_tx_fifo(i2c_dev);
>>> +
>>> +	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
>>> +		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
>>> +	if (i2c_dev->msg_read)
>>> +		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
>>> +	else if (i2c_dev->msg_buf_remaining)
>>> +		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
>>> +
>>> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
>>> +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
>>> +	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
>>> +		i2c_readl(i2c_dev, I2C_INT_MASK));
>>> +
>>> +	return 0;
>>> +}
>>> +
>>>  static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>>>  	struct i2c_msg *msg, enum msg_end_type end_state)  {
>>>  	u32 packet_header;
>>>  	u32 int_mask;
>>>  	unsigned long time_left;
>>> -	unsigned long flags;
>>> +	u32 *buffer;
>>> +	int ret = 0;
>>> +
>>> +	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
>>> +					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
>>> +	if (!buffer)
>>> +		return -ENOMEM;
>>
>> Isn't it possible to avoid "buffer" allocation / copying overhead and keep code as-is for the PIO mode?
>>
> 
> Keeping PIO mode code as is and adding DMA mode will have redundant code for header generation and msg complete timeout sections.
> Also in existing PIO mode implementation, TX FIFO is filled as soon as the word is ready but for DMA mode need to put all msg bytes along with header together to send thru DMA.
> So created common buffer to perform PIO/DMA to reduce redundancy.
> 

Okay, what about something like in this sketch:

...
	if (msg->flags & I2C_M_RD)
		xfer_size = msg->len;
	else
		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
			    I2C_PACKET_HEADER_SIZE;

	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);

	if (dma) {
		buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
				I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
		i2c_dev->msg_buf = (u8 *)buffer;
	}

	packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
				PACKET_HEADER0_PROTOCOL_I2C |
				(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
				(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

	if (dma)
		(*buffer++) = packet_header;
	else
		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

... and so on.

Likely that kmalloc overhead will be bigger than the above variant. Please consider to avoid kmalloc and copying for the PIO case in the next version.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 21:11         ` Sowjanya Komatineni
@ 2019-01-25 21:49           ` Dmitry Osipenko
  2019-01-25 22:09             ` Dmitry Osipenko
  2019-01-25 22:22             ` Sowjanya Komatineni
  2019-01-25 23:10           ` Thierry Reding
  1 sibling, 2 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 21:49 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

26.01.2019 0:11, Sowjanya Komatineni пишет:
> 
> 
>>>>> +	if (i2c_dev->has_dma) {
>>>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
>>>>> +		if (ret == -EPROBE_DEFER)
>>>>> +			goto disable_div_clk;
>>>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
>>>>> +		if (ret == -EPROBE_DEFER)
>>>>> +			goto disable_div_clk;
>>>>
>>>> So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.
>>>
>>> If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not available by the time tegra_init_dma_param tries to request slave channel and in those cases dma_request_slave_channel_reason will return EPROBE_DEFER for tegra I2C probe to retry
>>>
>>> In case if DMA is available but DMA buffer allocation fails, then tegra_i2c_init_dma_param returns ENOMEM and probe also fails returning same ENOMEM
>>
>> Is that what you're going to change in the next version? Your current variant of the code doesn't fail the probe on ENOMEM and there is duplicated attempt to invoke tegra_i2c_init_dma_param() during the transfer.
> 
> Sorry correction to my previous reply.  If DMA buffer allocation fails, tegra_i2c_init_dma_param returns ENOMEM but probe will succeed as i2c transaction need to happen during  boot for some platform device programming for successful boot and they use PIO mode as xfer bytes is less and deferring i2c probe for ENOMEM causes boot to fail so during probe EPROBE_DEFER is only taken care.
> 
> Re-attempt of tegra_i2c_init_dma_param in xfer happens only if no successful DMA channel allocation happens prior to that ( during probe in case of ENOMEM).
> DMA mode is mainly for large transfer, and i2c xfer returning failure due to failing DMA buffer allocation causes boot to hang as platform device programming need to happen which doesn’t need to use DMA.
> Will fix this and will send updated patch to reattempt DMA request and buffer allocation during DMA mode transfer and will return fail for DMA mode I2C transfer...

1) The chance to get ENOMEM during is miserable and likely that there are much bigger problems in this case. Hence just fail the probe if tegra_i2c_init_dma_param() fails with any error.

2) What is that super-critical platform device? It is possible to compile tegra-i2c as a kernel module and seems nothing enforces CONFIG_I2C_TEGRA=y.

3) After applying these patches I2C transfers are failing on Tegra20 with "tegra-i2c 7000c400.i2c: Failed to allocate the DMA buffer". 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-24 20:51   ` Sowjanya Komatineni
  (?)
  (?)
@ 2019-01-25 22:03   ` Dmitry Osipenko
  -1 siblings, 0 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 22:03 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch adds DMA support for Tegra I2C.
> 
> Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
> transfer size of the max FIFO depth and DMA mode is used for
> transfer size higher than max FIFO depth to save CPU overhead.
> 
> PIO mode needs full intervention of CPU to fill or empty FIFO's
> and also need to service multiple data requests interrupt for the
> same transaction adding overhead on CPU for large transfers.
> 
> DMA mode is helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 349 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 13bce1411ddc..769700d5a7f3 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -9,6 +9,9 @@
>  #include <asm/unaligned.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dmapool.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/err.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
> @@ -46,6 +49,8 @@
>  #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
>  #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
>  #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
> +#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
> +#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
>  #define I2C_FIFO_STATUS				0x060
>  #define I2C_FIFO_STATUS_TX_MASK			0xF0
>  #define I2C_FIFO_STATUS_TX_SHIFT		4
> @@ -120,6 +125,16 @@
>  /* Packet header size in bytes */
>  #define I2C_PACKET_HEADER_SIZE			12
>  
> +#define DATA_DMA_DIR_TX				(1 << 0)
> +#define DATA_DMA_DIR_RX				(1 << 1)
> +
> +/*
> + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
> + * above this, controller will use DMA to fill FIFO.
> + * MAX PIO len is 20 bytes excluding packet header.
> + */
> +#define I2C_PIO_MODE_MAX_LEN			32
> +
>  /*
>   * msg_end_type: The bus control which need to be send at end of transfer.
>   * @MSG_END_STOP: Send stop pulse at end of transfer.
> @@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
>   * @fast_clk: clock reference for fast clock of I2C controller
>   * @rst: reset control for the I2C controller
>   * @base: ioremapped registers cookie
> + * @phys_addr: Physical address of I2C base address to use for DMA configuration
>   * @cont_id: I2C controller ID, used for packet header
>   * @irq: IRQ number of transfer complete interrupt
>   * @irq_disabled: used to track whether or not the interrupt is enabled
> @@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
>   * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
>   * @is_multimaster_mode: track if I2C controller is in multi-master mode
>   * @xfer_lock: lock to serialize transfer submission and processing
> + * @has_dma: indicated if controller supports DMA
> + * @tx_dma_chan: DMA transmit channel
> + * @rx_dma_chan: DMA receive channel
> + * @dma_phys: handle to DMA resources
> + * @dma_buf: pointer to allocated DMA buffer
> + * @dma_buf_size: DMA buffer size
> + * @is_curr_dma_xfer: indicates active DMA transfer
> + * @dma_complete: DMA completion notifier
>   */
>  struct tegra_i2c_dev {
>  	struct device *dev;
> @@ -202,6 +226,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t phys_addr;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -215,8 +240,18 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	bool has_dma;
> +	struct dma_chan *tx_dma_chan;
> +	struct dma_chan *rx_dma_chan;
> +	dma_addr_t dma_phys;
> +	u32 *dma_buf;
> +	unsigned int dma_buf_size;
> +	bool is_curr_dma_xfer;
> +	struct completion dma_complete;
>  };
>  
> +static struct dma_chan *chan;
> +
>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>  		       unsigned long reg)
>  {
> @@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>  	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
>  }
>  
> +static void tegra_i2c_dma_complete(void *args)
> +{
> +	struct tegra_i2c_dev *i2c_dev = args;
> +
> +	complete(&i2c_dev->dma_complete);
> +}
> +
> +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
> +{
> +	struct dma_async_tx_descriptor *dma_desc;
> +	enum dma_transfer_direction dir;
> +
> +	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
> +	reinit_completion(&i2c_dev->dma_complete);
> +	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> +	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
> +					       len, dir, DMA_PREP_INTERRUPT |
> +					       DMA_CTRL_ACK);
> +	if (!dma_desc) {
> +		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
> +		return -EIO;
> +	}
> +
> +	dma_desc->callback = tegra_i2c_dma_complete;
> +	dma_desc->callback_param = i2c_dev;
> +	dmaengine_submit(dma_desc);
> +	dma_async_issue_pending(chan);
> +	return 0;
> +}
> +
> +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
> +				    bool dma_to_memory)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int ret;
> +	const char *chan_name = dma_to_memory ? "rx" : "tx";
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);
> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +				     &dma_phys, GFP_KERNEL);
> +
> +	if (!dma_buf) {
> +		dev_err(i2c_dev->dev, "Failed to allocate the DMA buffer\n");
> +		ret = -ENOMEM;
> +		goto scrub;
> +	}
> +
> +	if (dma_to_memory)
> +		i2c_dev->rx_dma_chan = dma_chan;
> +	else
> +		i2c_dev->tx_dma_chan = dma_chan;
> +
> +	i2c_dev->dma_buf = dma_buf;
> +	i2c_dev->dma_phys = dma_phys;
> +
> +	return 0;
> +
> +scrub:
> +	dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +			  dma_buf, dma_phys);

This will try to free dma_buf=NULL, remove the dma_free_coherent().

> +	dma_release_channel(dma_chan);
> +	return ret;
> +}
> +

[snip]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 21:49           ` Dmitry Osipenko
@ 2019-01-25 22:09             ` Dmitry Osipenko
  2019-01-25 22:22             ` Sowjanya Komatineni
  1 sibling, 0 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 22:09 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

26.01.2019 0:49, Dmitry Osipenko пишет:

> 3) After applying these patches I2C transfers are failing on Tegra20 with "tegra-i2c 7000c400.i2c: Failed to allocate the DMA buffer". 
> 

Actually scratch the above, turned out I haven't applied patches correctly. In fact patches are failing to apply with:

# git apply --verbose V2-3-4-i2c-tegra-Add-DMA-Support.patch
Checking patch drivers/i2c/busses/i2c-tegra.c...
Hunk #4 succeeded at 198 (offset 3 lines).
Hunk #5 succeeded at 212 (offset 3 lines).
Hunk #6 succeeded at 229 (offset 3 lines).
Hunk #7 succeeded at 243 (offset 3 lines).
Hunk #8 succeeded at 321 (offset 3 lines).
Hunk #9 succeeded at 636 (offset 3 lines).
Hunk #10 succeeded at 751 (offset 3 lines).
Hunk #11 succeeded at 798 (offset 3 lines).
Hunk #12 succeeded at 968 (offset 3 lines).
Hunk #13 succeeded at 996 (offset 3 lines).
Hunk #14 succeeded at 1050 (offset 3 lines).
Hunk #15 succeeded at 1075 (offset 3 lines).
Hunk #16 succeeded at 1125 (offset 3 lines).
Hunk #17 succeeded at 1180 (offset 3 lines).
Hunk #18 succeeded at 1304 (offset 13 lines).
error: while searching for:
                return -ENOMEM;

        i2c_dev->base = base;
        i2c_dev->div_clk = div_clk;
        i2c_dev->adapter.algo = &tegra_i2c_algo;
        i2c_dev->adapter.quirks = &tegra_i2c_quirks;
        i2c_dev->irq = irq;
        i2c_dev->cont_id = pdev->id;
        i2c_dev->dev = &pdev->dev;

        i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c");
        if (IS_ERR(i2c_dev->rst)) {

error: patch failed: drivers/i2c/busses/i2c-tegra.c:1000
error: drivers/i2c/busses/i2c-tegra.c: patch does not apply


Please use "git format-patch -4 xxxx" and do not edit patches manually.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 21:49           ` Dmitry Osipenko
  2019-01-25 22:09             ` Dmitry Osipenko
@ 2019-01-25 22:22             ` Sowjanya Komatineni
  1 sibling, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 22:22 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c



> >>>>> +	if (i2c_dev->has_dma) {
> >>>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> >>>>> +		if (ret == -EPROBE_DEFER)
> >>>>> +			goto disable_div_clk;
> >>>>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> >>>>> +		if (ret == -EPROBE_DEFER)
> >>>>> +			goto disable_div_clk;
> >>>>
> >>>> So tegra_i2c_init_dma_param() could fail, printing a error message, and probe will succeed? If allocation fails during the driver's probe, then just fail the probe. Please give the rationale.
> >>>
> >>> If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not available by the time tegra_init_dma_param tries to request slave channel and in those cases dma_request_slave_channel_reason will return EPROBE_DEFER for tegra I2C probe to retry
> >>>
> >>> In case if DMA is available but DMA buffer allocation fails, then tegra_i2c_init_dma_param returns ENOMEM and probe also fails returning same ENOMEM
> >>
> >> Is that what you're going to change in the next version? Your current variant of the code doesn't fail the probe on ENOMEM and there is duplicated attempt to invoke tegra_i2c_init_dma_param() during the transfer.
> > 
> > Sorry correction to my previous reply.  If DMA buffer allocation fails, tegra_i2c_init_dma_param returns ENOMEM but probe will succeed as i2c transaction need to happen during  boot for some platform device programming for successful boot and they use PIO > mode as xfer bytes is less and deferring i2c probe for ENOMEM causes boot to fail so during probe EPROBE_DEFER is only taken care.
> > 
> > Re-attempt of tegra_i2c_init_dma_param in xfer happens only if no successful DMA channel allocation happens prior to that ( during probe in case of ENOMEM).
> > DMA mode is mainly for large transfer, and i2c xfer returning failure due to failing DMA buffer allocation causes boot to hang as platform device programming need to happen which doesn’t need to use DMA.
> > Will fix this and will send updated patch to reattempt DMA request and buffer allocation during DMA mode transfer and will return fail for DMA mode I2C transfer...
>
> 1) The chance to get ENOMEM during is miserable and likely that there are much bigger problems in this case. Hence just fail the probe if tegra_i2c_init_dma_param() fails with any error.

Agree. 

> 2) What is that super-critical platform device? It is possible to compile tegra-i2c as a kernel module and seems nothing enforces CONFIG_I2C_TEGRA=y.
Some of the i2c device  access happens during boot for some tegra platforms like jetson tx1, tx2.. (Eg: I2C GPIO expanders, PMIC access...)

> 3) After applying these patches I2C transfers are failing on Tegra20 with "tegra-i2c 7000c400.i2c: Failed to allocate the DMA buffer". 
This patch should not cause DMA buffer allocation failure to happen. But if allocation fails (it couldn’t allocate contiguous physical memory for some reason), then transfers will definitely fail with this patch.

Will send updated patch with not re-attempt and in probe to return on error so if it will either defer or fail in case of ENOMEM.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-24 20:51   ` Sowjanya Komatineni
                     ` (2 preceding siblings ...)
  (?)
@ 2019-01-25 22:35   ` Dmitry Osipenko
  -1 siblings, 0 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 22:35 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch adds DMA support for Tegra I2C.
> 
> Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
> transfer size of the max FIFO depth and DMA mode is used for
> transfer size higher than max FIFO depth to save CPU overhead.
> 
> PIO mode needs full intervention of CPU to fill or empty FIFO's
> and also need to service multiple data requests interrupt for the
> same transaction adding overhead on CPU for large transfers.
> 
> DMA mode is helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 349 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 13bce1411ddc..769700d5a7f3 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -9,6 +9,9 @@
>  #include <asm/unaligned.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dmapool.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/err.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
> @@ -46,6 +49,8 @@
>  #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
>  #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
>  #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
> +#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
> +#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
>  #define I2C_FIFO_STATUS				0x060
>  #define I2C_FIFO_STATUS_TX_MASK			0xF0
>  #define I2C_FIFO_STATUS_TX_SHIFT		4
> @@ -120,6 +125,16 @@
>  /* Packet header size in bytes */
>  #define I2C_PACKET_HEADER_SIZE			12
>  
> +#define DATA_DMA_DIR_TX				(1 << 0)
> +#define DATA_DMA_DIR_RX				(1 << 1)
> +
> +/*
> + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
> + * above this, controller will use DMA to fill FIFO.
> + * MAX PIO len is 20 bytes excluding packet header.
> + */
> +#define I2C_PIO_MODE_MAX_LEN			32
> +
>  /*
>   * msg_end_type: The bus control which need to be send at end of transfer.
>   * @MSG_END_STOP: Send stop pulse at end of transfer.
> @@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
>   * @fast_clk: clock reference for fast clock of I2C controller
>   * @rst: reset control for the I2C controller
>   * @base: ioremapped registers cookie
> + * @phys_addr: Physical address of I2C base address to use for DMA configuration
>   * @cont_id: I2C controller ID, used for packet header
>   * @irq: IRQ number of transfer complete interrupt
>   * @irq_disabled: used to track whether or not the interrupt is enabled
> @@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
>   * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
>   * @is_multimaster_mode: track if I2C controller is in multi-master mode
>   * @xfer_lock: lock to serialize transfer submission and processing
> + * @has_dma: indicated if controller supports DMA
> + * @tx_dma_chan: DMA transmit channel
> + * @rx_dma_chan: DMA receive channel
> + * @dma_phys: handle to DMA resources
> + * @dma_buf: pointer to allocated DMA buffer
> + * @dma_buf_size: DMA buffer size
> + * @is_curr_dma_xfer: indicates active DMA transfer
> + * @dma_complete: DMA completion notifier
>   */
>  struct tegra_i2c_dev {
>  	struct device *dev;
> @@ -202,6 +226,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t phys_addr;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -215,8 +240,18 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	bool has_dma;
> +	struct dma_chan *tx_dma_chan;
> +	struct dma_chan *rx_dma_chan;
> +	dma_addr_t dma_phys;
> +	u32 *dma_buf;
> +	unsigned int dma_buf_size;
> +	bool is_curr_dma_xfer;
> +	struct completion dma_complete;
>  };
>  
> +static struct dma_chan *chan;
> +
>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>  		       unsigned long reg)
>  {
> @@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>  	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
>  }
>  
> +static void tegra_i2c_dma_complete(void *args)
> +{
> +	struct tegra_i2c_dev *i2c_dev = args;
> +
> +	complete(&i2c_dev->dma_complete);
> +}
> +
> +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
> +{
> +	struct dma_async_tx_descriptor *dma_desc;
> +	enum dma_transfer_direction dir;
> +
> +	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
> +	reinit_completion(&i2c_dev->dma_complete);
> +	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> +	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
> +					       len, dir, DMA_PREP_INTERRUPT |
> +					       DMA_CTRL_ACK);
> +	if (!dma_desc) {
> +		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
> +		return -EIO;
> +	}
> +
> +	dma_desc->callback = tegra_i2c_dma_complete;
> +	dma_desc->callback_param = i2c_dev;
> +	dmaengine_submit(dma_desc);
> +	dma_async_issue_pending(chan);
> +	return 0;
> +}
> +
> +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
> +				    bool dma_to_memory)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int ret;
> +	const char *chan_name = dma_to_memory ? "rx" : "tx";
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);
> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +				     &dma_phys, GFP_KERNEL);
> +
> +	if (!dma_buf) {
> +		dev_err(i2c_dev->dev, "Failed to allocate the DMA buffer\n");
> +		ret = -ENOMEM;
> +		goto scrub;
> +	}
> +
> +	if (dma_to_memory)
> +		i2c_dev->rx_dma_chan = dma_chan;
> +	else
> +		i2c_dev->tx_dma_chan = dma_chan;
> +
> +	i2c_dev->dma_buf = dma_buf;
> +	i2c_dev->dma_phys = dma_phys;
> +
> +	return 0;
> +
> +scrub:
> +	dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +			  dma_buf, dma_phys);
> +	dma_release_channel(dma_chan);
> +	return ret;
> +}
> +
>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>  {
>  	unsigned long timeout = jiffies + HZ;
> @@ -529,6 +633,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  		return err;
>  	}
>  
> +	i2c_dev->is_curr_dma_xfer = false;
> +
>  	reset_control_assert(i2c_dev->rst);
>  	udelay(2);
>  	reset_control_deassert(i2c_dev->rst);
> @@ -642,25 +748,45 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  		goto err;
>  	}
>  
> -	if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
> -		if (i2c_dev->msg_buf_remaining)
> -			tegra_i2c_empty_rx_fifo(i2c_dev);
> -		else
> -			BUG();
> -	}
> +	if (!i2c_dev->is_curr_dma_xfer) {
> +		if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
> +			if (i2c_dev->msg_buf_remaining)
> +				tegra_i2c_empty_rx_fifo(i2c_dev);
> +			else
> +				BUG();
> +		}
>  
> -	if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
> -		if (i2c_dev->msg_buf_remaining)
> -			tegra_i2c_fill_tx_fifo(i2c_dev);
> -		else
> -			tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
> +		if (!i2c_dev->msg_read &&
> +		   (status & I2C_INT_TX_FIFO_DATA_REQ)) {
> +			if (i2c_dev->msg_buf_remaining)
> +				tegra_i2c_fill_tx_fifo(i2c_dev);
> +			else
> +				tegra_i2c_mask_irq(i2c_dev,
> +						   I2C_INT_TX_FIFO_DATA_REQ);
> +		}
>  	}
>  
>  	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> -	if (status & I2C_INT_PACKET_XFER_COMPLETE) {
> +	if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) {
> +	/*
> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
> +	 * DMA complete notification and during message write XFER_COMPLETE
> +	 * interrupt is triggered after DMA complete notification.
> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
> +	 * so forcing msg_buf_remaining to 0.
> +	 */
> +		if (i2c_dev->is_curr_dma_xfer)
> +			i2c_dev->msg_buf_remaining = 0;
> +		status |= I2C_INT_PACKET_XFER_COMPLETE;
> +		i2c_writel(i2c_dev, status, I2C_INT_STATUS);
> +		if (!i2c_dev->msg_buf_remaining)
> +			complete(&i2c_dev->msg_complete);
> +	} else if (status & I2C_INT_PACKET_XFER_COMPLETE) {
> +		if (i2c_dev->is_curr_dma_xfer)
> +			i2c_dev->msg_buf_remaining = 0;
>  		BUG_ON(i2c_dev->msg_buf_remaining);
>  		complete(&i2c_dev->msg_complete);
>  	}
> @@ -669,17 +795,161 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	/* An error occurred, mask all interrupts */
>  	tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
>  		I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
> -		I2C_INT_RX_FIFO_DATA_REQ);
> +		I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_ALL_PACKETS_XFER_COMPLETE);
>  	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> +	if (i2c_dev->is_curr_dma_xfer) {
> +		dmaengine_terminate_all(chan);
> +		complete(&i2c_dev->dma_complete);
> +	}
> +
>  	complete(&i2c_dev->msg_complete);
>  done:
>  	spin_unlock(&i2c_dev->xfer_lock);
>  	return IRQ_HANDLED;
>  }
>  
> +static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
> +				       size_t len, int direction)
> +{
> +	u32 val, reg;
> +	u8 dma_burst = 0;
> +	struct dma_slave_config dma_sconfig;
> +
> +	if (i2c_dev->hw->has_mst_fifo)
> +		reg = I2C_MST_FIFO_CONTROL;
> +	else
> +		reg = I2C_FIFO_CONTROL;
> +	val = i2c_readl(i2c_dev, reg);
> +
> +	if (len & 0xF)
> +		dma_burst = 1;
> +	else if (len & 0x10)
> +		dma_burst = 4;
> +	else
> +		dma_burst = 8;
> +

Won't it be more optimal to always transfer DMA with dma_burst=8 and then the leftover nibbles in PIO? 


[snip]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 21:11         ` Sowjanya Komatineni
  2019-01-25 21:49           ` Dmitry Osipenko
@ 2019-01-25 23:10           ` Thierry Reding
  2019-01-25 23:29             ` Sowjanya Komatineni
  1 sibling, 1 reply; 28+ messages in thread
From: Thierry Reding @ 2019-01-25 23:10 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: Dmitry Osipenko, Jonathan Hunter, Mantravadi Karthik,
	Shardar Mohammed, Timo Alho, linux-tegra, linux-kernel,
	linux-i2c

[-- Attachment #1: Type: text/plain, Size: 2594 bytes --]

On Fri, Jan 25, 2019 at 09:11:54PM +0000, Sowjanya Komatineni wrote:
> 
> 
> > >>> +	if (i2c_dev->has_dma) {
> > >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> > >>> +		if (ret == -EPROBE_DEFER)
> > >>> +			goto disable_div_clk;
> > >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> > >>> +		if (ret == -EPROBE_DEFER)
> > >>> +			goto disable_div_clk;
> > >>
> > >> So tegra_i2c_init_dma_param() could fail, printing a error
> > >> message, and probe will succeed? If allocation fails during the
> > >> driver's probe, then just fail the probe. Please give the
> > >> rationale.
> > > 
> > > If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not
> > > available by the time tegra_init_dma_param tries to request slave
> > > channel and in those cases dma_request_slave_channel_reason will
> > > return EPROBE_DEFER for tegra I2C probe to retry
> > > 
> > > In case if DMA is available but DMA buffer allocation fails, then
> > > tegra_i2c_init_dma_param returns ENOMEM and probe also fails
> > > returning same ENOMEM
> >
> > Is that what you're going to change in the next version? Your
> > current variant of the code doesn't fail the probe on ENOMEM and
> > there is duplicated attempt to invoke tegra_i2c_init_dma_param()
> > during the transfer.
> 
> Sorry correction to my previous reply.  If DMA buffer allocation
> fails, tegra_i2c_init_dma_param returns ENOMEM but probe will succeed
> as i2c transaction need to happen during  boot for some platform
> device programming for successful boot and they use PIO mode as xfer
> bytes is less and deferring i2c probe for ENOMEM causes boot to fail
> so during probe EPROBE_DEFER is only taken care.
> 
> Re-attempt of tegra_i2c_init_dma_param in xfer happens only if no
> successful DMA channel allocation happens prior to that ( during probe
> in case of ENOMEM).
> DMA mode is mainly for large transfer, and i2c xfer returning failure
> due to failing DMA buffer allocation causes boot to hang as platform
> device programming need to happen which doesn’t need to use DMA.
> Will fix this and will send updated patch to reattempt DMA request and
> buffer allocation during DMA mode transfer and will return fail for
> DMA mode I2C transfer...

I'm wondering if we shouldn't gracefully to PIO if we fail to allocate
DMA buffers. Even if it is unlikely that large transfers (> than the
threshold that activates the DMA paths) will be needed by critical I2C
clients, there's really no reason why we can't do PIO if DMA doesn't
work for some reason.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-25 21:25       ` Dmitry Osipenko
@ 2019-01-25 23:22         ` Dmitry Osipenko
  2019-01-25 23:26           ` Sowjanya Komatineni
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-25 23:22 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

26.01.2019 0:25, Dmitry Osipenko пишет:
> 25.01.2019 23:20, Sowjanya Komatineni пишет:
>>
>>
>>>> This patch prepares the buffer with the message bytes to be 
>>>> transmitted along with the packet header information and then performs 
>>>> i2c transfer in PIO mode.
>>>>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>> ---
>>>>  [V2] : DMA support changes include preparing buffer with message bytes and
>>>> 	and header before sending them through DMA. So splitted the whole
>>>> 	change into 2 seperate patches in this series.
>>>>
>>>>  drivers/i2c/busses/i2c-tegra.c | 97 
>>>> +++++++++++++++++++++++++++++-------------
>>>>  1 file changed, 68 insertions(+), 29 deletions(-)
>>>>
>>>> diff --git a/drivers/i2c/busses/i2c-tegra.c 
>>>> b/drivers/i2c/busses/i2c-tegra.c index ef854be4c837..13bce1411ddc 
>>>> 100644
>>>> --- a/drivers/i2c/busses/i2c-tegra.c
>>>> +++ b/drivers/i2c/busses/i2c-tegra.c
>>>> @@ -117,6 +117,9 @@
>>>>  #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
>>>>  #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
>>>>  
>>>> +/* Packet header size in bytes */
>>>> +#define I2C_PACKET_HEADER_SIZE			12
>>>> +
>>>>  /*
>>>>   * msg_end_type: The bus control which need to be send at end of transfer.
>>>>   * @MSG_END_STOP: Send stop pulse at end of transfer.
>>>> @@ -677,35 +680,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>>>  	return IRQ_HANDLED;
>>>>  }
>>>>  
>>>> +static int tegra_i2c_start_pio_xfer(struct tegra_i2c_dev *i2c_dev) {
>>>> +	u32 *buffer = (u32 *)i2c_dev->msg_buf;
>>>> +	unsigned long flags;
>>>> +	u32 int_mask;
>>>> +
>>>> +	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
>>>> +
>>>> +	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
>>>> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
>>>> +
>>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>>> +	i2c_writel(i2c_dev, *(buffer++), I2C_TX_FIFO);
>>>> +
>>>> +	i2c_dev->msg_buf = (u8 *) buffer;
>>>> +
>>>> +	if (!i2c_dev->msg_read)
>>>> +		tegra_i2c_fill_tx_fifo(i2c_dev);
>>>> +
>>>> +	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
>>>> +		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
>>>> +	if (i2c_dev->msg_read)
>>>> +		int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
>>>> +	else if (i2c_dev->msg_buf_remaining)
>>>> +		int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
>>>> +
>>>> +	tegra_i2c_unmask_irq(i2c_dev, int_mask);
>>>> +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
>>>> +	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
>>>> +		i2c_readl(i2c_dev, I2C_INT_MASK));
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>  static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>>>>  	struct i2c_msg *msg, enum msg_end_type end_state)  {
>>>>  	u32 packet_header;
>>>>  	u32 int_mask;
>>>>  	unsigned long time_left;
>>>> -	unsigned long flags;
>>>> +	u32 *buffer;
>>>> +	int ret = 0;
>>>> +
>>>> +	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
>>>> +					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
>>>> +	if (!buffer)
>>>> +		return -ENOMEM;
>>>
>>> Isn't it possible to avoid "buffer" allocation / copying overhead and keep code as-is for the PIO mode?
>>>
>>
>> Keeping PIO mode code as is and adding DMA mode will have redundant code for header generation and msg complete timeout sections.
>> Also in existing PIO mode implementation, TX FIFO is filled as soon as the word is ready but for DMA mode need to put all msg bytes along with header together to send thru DMA.
>> So created common buffer to perform PIO/DMA to reduce redundancy.
>>
> 
> Okay, what about something like in this sketch:
> 
> ...
> 	if (msg->flags & I2C_M_RD)
> 		xfer_size = msg->len;
> 	else
> 		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> 			    I2C_PACKET_HEADER_SIZE;
> 
> 	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
> 		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);
> 
> 	if (dma) {
> 		buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> 				I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> 		i2c_dev->msg_buf = (u8 *)buffer;
> 	}
> 
> 	packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
> 				PACKET_HEADER0_PROTOCOL_I2C |
> 				(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
> 				(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
> 		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> 
> 	if (dma)
> 		(*buffer++) = packet_header;
> 	else
> 		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> 
> ... and so on.
> 
> Likely that kmalloc overhead will be bigger than the above variant. Please consider to avoid kmalloc and copying for the PIO case in the next version.
> 

Also, is the intermediate kmalloc "buffer" really needed at all? Why just not to write directly into the DMA buffer?

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer
  2019-01-25 23:22         ` Dmitry Osipenko
@ 2019-01-25 23:26           ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 23:26 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c


> >>>> +	buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> >>>> +					I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> >>>> +	if (!buffer)
> >>>> +		return -ENOMEM;
> >>>
> >>> Isn't it possible to avoid "buffer" allocation / copying overhead and keep code as-is for the PIO mode?
> >>>
> >>
> >> Keeping PIO mode code as is and adding DMA mode will have redundant code for header generation and msg complete timeout sections.
> >> Also in existing PIO mode implementation, TX FIFO is filled as soon as the word is ready but for DMA mode need to put all msg bytes along with header together to send thru DMA.
> >> So created common buffer to perform PIO/DMA to reduce redundancy.
> >>
> > 
> > Okay, what about something like in this sketch:
> > 
> > ...
> > 	if (msg->flags & I2C_M_RD)
> > 		xfer_size = msg->len;
> > 	else
> > 		xfer_size = ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> > 			    I2C_PACKET_HEADER_SIZE;
> > 
> > 	dma = ((xfer_size > I2C_PIO_MODE_MAX_LEN) &&
> > 		i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan);
> > 
> > 	if (dma) {
> > 		buffer = kmalloc(ALIGN(msg->len, BYTES_PER_FIFO_WORD) +
> > 				I2C_PACKET_HEADER_SIZE, GFP_KERNEL);
> > 		i2c_dev->msg_buf = (u8 *)buffer;
> > 	}
> > 
> > 	packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
> > 				PACKET_HEADER0_PROTOCOL_I2C |
> > 				(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
> > 				(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
> > 		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> > 
> > 	if (dma)
> > 		(*buffer++) = packet_header;
> > 	else
> > 		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> > 
> > ... and so on.
> > 
> > Likely that kmalloc overhead will be bigger than the above variant. Please consider to avoid kmalloc and copying for the PIO case in the next version.
> > 
>
> Also, is the intermediate kmalloc "buffer" really needed at all? Why just not to write directly into the DMA buffer?

Yes, that’s what I am thinking too. Changing it. Will send revised patch..


^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-25 23:10           ` Thierry Reding
@ 2019-01-25 23:29             ` Sowjanya Komatineni
  0 siblings, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-25 23:29 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Dmitry Osipenko, Jonathan Hunter, Mantravadi Karthik,
	Shardar Mohammed, Timo Alho, linux-tegra, linux-kernel,
	linux-i2c



> > > >>> +	if (i2c_dev->has_dma) {
> > > >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> > > >>> +		if (ret == -EPROBE_DEFER)
> > > >>> +			goto disable_div_clk;
> > > >>> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> > > >>> +		if (ret == -EPROBE_DEFER)
> > > >>> +			goto disable_div_clk;
> > > >>
> > > >> So tegra_i2c_init_dma_param() could fail, printing a error 
> > > >> message, and probe will succeed? If allocation fails during the 
> > > >> driver's probe, then just fail the probe. Please give the 
> > > >> rationale.
> > > > 
> > > > If APB DMA probe doesn’t happen prior to tegra i2c, DMA is not 
> > > > available by the time tegra_init_dma_param tries to request slave 
> > > > channel and in those cases dma_request_slave_channel_reason will 
> > > > return EPROBE_DEFER for tegra I2C probe to retry
> > > > 
> > > > In case if DMA is available but DMA buffer allocation fails, then 
> > > > tegra_i2c_init_dma_param returns ENOMEM and probe also fails 
> > > > returning same ENOMEM
> > >
> > > Is that what you're going to change in the next version? Your 
> > > current variant of the code doesn't fail the probe on ENOMEM and 
> > > there is duplicated attempt to invoke tegra_i2c_init_dma_param() 
> > > during the transfer.
> > 
> > Sorry correction to my previous reply.  If DMA buffer allocation 
> > fails, tegra_i2c_init_dma_param returns ENOMEM but probe will succeed 
> > as i2c transaction need to happen during  boot for some platform 
> > device programming for successful boot and they use PIO mode as xfer 
> > bytes is less and deferring i2c probe for ENOMEM causes boot to fail 
> > so during probe EPROBE_DEFER is only taken care.
> > 
> > Re-attempt of tegra_i2c_init_dma_param in xfer happens only if no 
> > successful DMA channel allocation happens prior to that ( during probe 
> > in case of ENOMEM).
> > DMA mode is mainly for large transfer, and i2c xfer returning failure 
> > due to failing DMA buffer allocation causes boot to hang as platform 
> > device programming need to happen which doesn’t need to use DMA.
> > Will fix this and will send updated patch to reattempt DMA request and 
> > buffer allocation during DMA mode transfer and will return fail for 
> > DMA mode I2C transfer...
>
> I'm wondering if we shouldn't gracefully to PIO if we fail to allocate DMA buffers. Even if it is unlikely that large transfers (> than the threshold that activates the DMA paths) will be needed by critical I2C clients, there's really no reason why we can't do PIO if DMA > doesn't work for some reason.
>
> Thierry

Yes we can fall back to PIO mode incase of DMA buffer allocation failure. Will add this in next revised patch

Sowjanya

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-24 20:51   ` Sowjanya Komatineni
                     ` (3 preceding siblings ...)
  (?)
@ 2019-01-26  0:28   ` Dmitry Osipenko
  2019-01-26  0:49     ` Dmitry Osipenko
  2019-01-26  1:51     ` Sowjanya Komatineni
  -1 siblings, 2 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-26  0:28 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

24.01.2019 23:51, Sowjanya Komatineni пишет:
> This patch adds DMA support for Tegra I2C.
> 
> Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
> transfer size of the max FIFO depth and DMA mode is used for
> transfer size higher than max FIFO depth to save CPU overhead.
> 
> PIO mode needs full intervention of CPU to fill or empty FIFO's
> and also need to service multiple data requests interrupt for the
> same transaction adding overhead on CPU for large transfers.
> 
> DMA mode is helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 349 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 13bce1411ddc..769700d5a7f3 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -9,6 +9,9 @@
>  #include <asm/unaligned.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dmapool.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/err.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
> @@ -46,6 +49,8 @@
>  #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
>  #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
>  #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
> +#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
> +#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
>  #define I2C_FIFO_STATUS				0x060
>  #define I2C_FIFO_STATUS_TX_MASK			0xF0
>  #define I2C_FIFO_STATUS_TX_SHIFT		4
> @@ -120,6 +125,16 @@
>  /* Packet header size in bytes */
>  #define I2C_PACKET_HEADER_SIZE			12
>  
> +#define DATA_DMA_DIR_TX				(1 << 0)
> +#define DATA_DMA_DIR_RX				(1 << 1)
> +
> +/*
> + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
> + * above this, controller will use DMA to fill FIFO.
> + * MAX PIO len is 20 bytes excluding packet header.
> + */
> +#define I2C_PIO_MODE_MAX_LEN			32
> +
>  /*
>   * msg_end_type: The bus control which need to be send at end of transfer.
>   * @MSG_END_STOP: Send stop pulse at end of transfer.
> @@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
>   * @fast_clk: clock reference for fast clock of I2C controller
>   * @rst: reset control for the I2C controller
>   * @base: ioremapped registers cookie
> + * @phys_addr: Physical address of I2C base address to use for DMA configuration
>   * @cont_id: I2C controller ID, used for packet header
>   * @irq: IRQ number of transfer complete interrupt
>   * @irq_disabled: used to track whether or not the interrupt is enabled
> @@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
>   * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
>   * @is_multimaster_mode: track if I2C controller is in multi-master mode
>   * @xfer_lock: lock to serialize transfer submission and processing
> + * @has_dma: indicated if controller supports DMA
> + * @tx_dma_chan: DMA transmit channel
> + * @rx_dma_chan: DMA receive channel
> + * @dma_phys: handle to DMA resources
> + * @dma_buf: pointer to allocated DMA buffer
> + * @dma_buf_size: DMA buffer size
> + * @is_curr_dma_xfer: indicates active DMA transfer
> + * @dma_complete: DMA completion notifier
>   */
>  struct tegra_i2c_dev {
>  	struct device *dev;
> @@ -202,6 +226,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t phys_addr;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -215,8 +240,18 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	bool has_dma;
> +	struct dma_chan *tx_dma_chan;
> +	struct dma_chan *rx_dma_chan;
> +	dma_addr_t dma_phys;
> +	u32 *dma_buf;
> +	unsigned int dma_buf_size;
> +	bool is_curr_dma_xfer;
> +	struct completion dma_complete;
>  };
>  
> +static struct dma_chan *chan;
> +
>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>  		       unsigned long reg)
>  {
> @@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>  	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
>  }
>  
> +static void tegra_i2c_dma_complete(void *args)
> +{
> +	struct tegra_i2c_dev *i2c_dev = args;
> +
> +	complete(&i2c_dev->dma_complete);
> +}
> +
> +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
> +{
> +	struct dma_async_tx_descriptor *dma_desc;
> +	enum dma_transfer_direction dir;
> +
> +	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
> +	reinit_completion(&i2c_dev->dma_complete);
> +	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> +	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
> +					       len, dir, DMA_PREP_INTERRUPT |
> +					       DMA_CTRL_ACK);
> +	if (!dma_desc) {
> +		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
> +		return -EIO;
> +	}
> +
> +	dma_desc->callback = tegra_i2c_dma_complete;
> +	dma_desc->callback_param = i2c_dev;
> +	dmaengine_submit(dma_desc);
> +	dma_async_issue_pending(chan);
> +	return 0;
> +}
> +
> +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
> +				    bool dma_to_memory)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int ret;
> +	const char *chan_name = dma_to_memory ? "rx" : "tx";
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);

Here shall be a check of whether dma_buf is already allocated, otherwise it will be allocated twice and the first allocation turned into memleak:

	if (i2c_dev->dma_buf)
		return 0;

> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +				     &dma_phys, GFP_KERNEL);
> +

I'm wondering whether a write-combined DMA mapping will be more optimal than having L2 flushes / invalidation for the "coherent" allocations. 

And I'm now questioning whether there is any real benefit from the DMA transferring at all, given the DMA bounce-buffer CPU read/write overhead. It looks to me that the whole purpose of the I2C DMA transferring is to move data from (to) some I2C device to a some device on the APB bus, bypassing the CPU. If that's is the case, then this patch may not really worth the effort and instead could only hurt the transferring performance. Please provide some performance results or correct me if I'm wrong.

[snip]

>  
>  	if (!i2c_dev->hw->has_single_clk_source) {
>  		fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
> @@ -1079,6 +1402,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> +	if (i2c_dev->has_dma) {
> +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> +		if (ret == -EPROBE_DEFER)
> +			goto disable_div_clk;
> +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> +		if (ret == -EPROBE_DEFER)
> +			goto disable_div_clk;

Missing dma_buf freeing and channel releasing in a case of error.

> +	}
> +
>  	ret = tegra_i2c_init(i2c_dev);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-26  0:28   ` Dmitry Osipenko
@ 2019-01-26  0:49     ` Dmitry Osipenko
  2019-01-26  1:51     ` Sowjanya Komatineni
  1 sibling, 0 replies; 28+ messages in thread
From: Dmitry Osipenko @ 2019-01-26  0:49 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

26.01.2019 3:28, Dmitry Osipenko пишет:
> 24.01.2019 23:51, Sowjanya Komatineni пишет:
>> This patch adds DMA support for Tegra I2C.
>>
>> Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
>> transfer size of the max FIFO depth and DMA mode is used for
>> transfer size higher than max FIFO depth to save CPU overhead.
>>
>> PIO mode needs full intervention of CPU to fill or empty FIFO's
>> and also need to service multiple data requests interrupt for the
>> same transaction adding overhead on CPU for large transfers.
>>
>> DMA mode is helpful for Large transfers during downloading or
>> uploading FW over I2C to some external devices.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>  [V2] : Updated based on V1 review feedback along with code cleanup for
>> 	proper implementation of DMA.
>>
>>  drivers/i2c/busses/i2c-tegra.c | 366 +++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 349 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
>> index 13bce1411ddc..769700d5a7f3 100644
>> --- a/drivers/i2c/busses/i2c-tegra.c
>> +++ b/drivers/i2c/busses/i2c-tegra.c
>> @@ -9,6 +9,9 @@
>>  #include <asm/unaligned.h>
>>  #include <linux/clk.h>
>>  #include <linux/delay.h>
>> +#include <linux/dmaengine.h>
>> +#include <linux/dmapool.h>
>> +#include <linux/dma-mapping.h>
>>  #include <linux/err.h>
>>  #include <linux/i2c.h>
>>  #include <linux/init.h>
>> @@ -46,6 +49,8 @@
>>  #define I2C_FIFO_CONTROL_RX_FLUSH		BIT(0)
>>  #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT		5
>>  #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT		2
>> +#define I2C_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 5)
>> +#define I2C_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) << 2)
>>  #define I2C_FIFO_STATUS				0x060
>>  #define I2C_FIFO_STATUS_TX_MASK			0xF0
>>  #define I2C_FIFO_STATUS_TX_SHIFT		4
>> @@ -120,6 +125,16 @@
>>  /* Packet header size in bytes */
>>  #define I2C_PACKET_HEADER_SIZE			12
>>  
>> +#define DATA_DMA_DIR_TX				(1 << 0)
>> +#define DATA_DMA_DIR_RX				(1 << 1)
>> +
>> +/*
>> + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
>> + * above this, controller will use DMA to fill FIFO.
>> + * MAX PIO len is 20 bytes excluding packet header.
>> + */
>> +#define I2C_PIO_MODE_MAX_LEN			32
>> +
>>  /*
>>   * msg_end_type: The bus control which need to be send at end of transfer.
>>   * @MSG_END_STOP: Send stop pulse at end of transfer.
>> @@ -180,6 +195,7 @@ struct tegra_i2c_hw_feature {
>>   * @fast_clk: clock reference for fast clock of I2C controller
>>   * @rst: reset control for the I2C controller
>>   * @base: ioremapped registers cookie
>> + * @phys_addr: Physical address of I2C base address to use for DMA configuration
>>   * @cont_id: I2C controller ID, used for packet header
>>   * @irq: IRQ number of transfer complete interrupt
>>   * @irq_disabled: used to track whether or not the interrupt is enabled
>> @@ -193,6 +209,14 @@ struct tegra_i2c_hw_feature {
>>   * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
>>   * @is_multimaster_mode: track if I2C controller is in multi-master mode
>>   * @xfer_lock: lock to serialize transfer submission and processing
>> + * @has_dma: indicated if controller supports DMA
>> + * @tx_dma_chan: DMA transmit channel
>> + * @rx_dma_chan: DMA receive channel
>> + * @dma_phys: handle to DMA resources
>> + * @dma_buf: pointer to allocated DMA buffer
>> + * @dma_buf_size: DMA buffer size
>> + * @is_curr_dma_xfer: indicates active DMA transfer
>> + * @dma_complete: DMA completion notifier
>>   */
>>  struct tegra_i2c_dev {
>>  	struct device *dev;
>> @@ -202,6 +226,7 @@ struct tegra_i2c_dev {
>>  	struct clk *fast_clk;
>>  	struct reset_control *rst;
>>  	void __iomem *base;
>> +	phys_addr_t phys_addr;
>>  	int cont_id;
>>  	int irq;
>>  	bool irq_disabled;
>> @@ -215,8 +240,18 @@ struct tegra_i2c_dev {
>>  	u16 clk_divisor_non_hs_mode;
>>  	bool is_multimaster_mode;
>>  	spinlock_t xfer_lock;
>> +	bool has_dma;
>> +	struct dma_chan *tx_dma_chan;
>> +	struct dma_chan *rx_dma_chan;
>> +	dma_addr_t dma_phys;
>> +	u32 *dma_buf;
>> +	unsigned int dma_buf_size;
>> +	bool is_curr_dma_xfer;
>> +	struct completion dma_complete;
>>  };
>>  
>> +static struct dma_chan *chan;
>> +
>>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>>  		       unsigned long reg)
>>  {
>> @@ -283,6 +318,75 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>>  	i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
>>  }
>>  
>> +static void tegra_i2c_dma_complete(void *args)
>> +{
>> +	struct tegra_i2c_dev *i2c_dev = args;
>> +
>> +	complete(&i2c_dev->dma_complete);
>> +}
>> +
>> +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
>> +{
>> +	struct dma_async_tx_descriptor *dma_desc;
>> +	enum dma_transfer_direction dir;
>> +
>> +	dev_dbg(i2c_dev->dev, "Starting DMA for length: %zu\n", len);
>> +	reinit_completion(&i2c_dev->dma_complete);
>> +	dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
>> +	dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
>> +					       len, dir, DMA_PREP_INTERRUPT |
>> +					       DMA_CTRL_ACK);
>> +	if (!dma_desc) {
>> +		dev_err(i2c_dev->dev, "Failed to get DMA descriptor\n");
>> +		return -EIO;
>> +	}
>> +
>> +	dma_desc->callback = tegra_i2c_dma_complete;
>> +	dma_desc->callback_param = i2c_dev;
>> +	dmaengine_submit(dma_desc);
>> +	dma_async_issue_pending(chan);
>> +	return 0;
>> +}
>> +
>> +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
>> +				    bool dma_to_memory)
>> +{
>> +	struct dma_chan *dma_chan;
>> +	u32 *dma_buf;
>> +	dma_addr_t dma_phys;
>> +	int ret;
>> +	const char *chan_name = dma_to_memory ? "rx" : "tx";
>> +
>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
>> +	if (IS_ERR(dma_chan))
>> +		return PTR_ERR(dma_chan);
> 
> Here shall be a check of whether dma_buf is already allocated, otherwise it will be allocated twice and the first allocation turned into memleak:
> 
> 	if (i2c_dev->dma_buf)
> 		return 0;
> 
>> +
>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
>> +				     &dma_phys, GFP_KERNEL);
>> +
> 
> I'm wondering whether a write-combined DMA mapping will be more optimal than having L2 flushes / invalidation for the "coherent" allocations. 
> 
> And I'm now questioning whether there is any real benefit from the DMA transferring at all, given the DMA bounce-buffer CPU read/write overhead. It looks to me that the whole purpose of the I2C DMA transferring is to move data from (to) some I2C device to a some device on the APB bus, bypassing the CPU. If that's is the case, then this patch may not really worth the effort and instead could only hurt the transferring performance. Please provide some performance results or correct me if I'm wrong.

And probably the last thought for today.. Could APB DMA access IRAM? If yes, then it becomes more interesting.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH V2 3/4] i2c: tegra: Add DMA Support
  2019-01-26  0:28   ` Dmitry Osipenko
  2019-01-26  0:49     ` Dmitry Osipenko
@ 2019-01-26  1:51     ` Sowjanya Komatineni
  1 sibling, 0 replies; 28+ messages in thread
From: Sowjanya Komatineni @ 2019-01-26  1:51 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

> > +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev,
> > +				    bool dma_to_memory)
> > +{
> > +	struct dma_chan *dma_chan;
> > +	u32 *dma_buf;
> > +	dma_addr_t dma_phys;
> > +	int ret;
> > +	const char *chan_name = dma_to_memory ? "rx" : "tx";
> > +
> > +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, chan_name);
> > +	if (IS_ERR(dma_chan))
> > +		return PTR_ERR(dma_chan);
>
> Here shall be a check of whether dma_buf is already allocated, otherwise it will be allocated twice and the first allocation turned into memleak:
>
>	if (i2c_dev->dma_buf)
>		return 0;

If dma_buf is allocated already then dma channels will be assigned and it will not perform init dma again.
So if dma buf allocation happens already then it will not come here again.

> > +
> > +	dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> > +				     &dma_phys, GFP_KERNEL);
> > +
>
> I'm wondering whether a write-combined DMA mapping will be more optimal than having L2 flushes / invalidation for the "coherent" allocations. 
>
> And I'm now questioning whether there is any real benefit from the DMA transferring at all, given the DMA bounce-buffer CPU read/write overhead. It looks to me that the whole purpose of the I2C DMA transferring is to move data from (to) some I2C device to a > > some device on the APB bus, bypassing the CPU. If that's is the case, then this patch may not really worth the effort and instead could only hurt the transferring performance. Please provide some performance results or correct me if I'm wrong.
>
> [snip]

DMA transfer helps to reduce overhead only during large transfer which is very rare operation. Will check and provide some performance data...

> >  
> >  	if (!i2c_dev->hw->has_single_clk_source) {
> >  		fast_clk = devm_clk_get(&pdev->dev, "fast-clk"); @@ -1079,6 
> > +1402,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
> >  		}
> >  	}
> >  
> > +	if (i2c_dev->has_dma) {
> > +		ret = tegra_i2c_init_dma_param(i2c_dev, true);
> > +		if (ret == -EPROBE_DEFER)
> > +			goto disable_div_clk;
> > +		ret = tegra_i2c_init_dma_param(i2c_dev, false);
> > +		if (ret == -EPROBE_DEFER)
> > +			goto disable_div_clk;
>
> Missing dma_buf freeing and channel releasing in a case of error.

dma buf freeing and channel release happens inside init_dma when buffer allocation fails.
If dma_request_slave_channel_reason fails no need to release channel but if channel request succeeds and buffer allocation fails, then buffer free and channel release will happen.

> > +	}
> > +
> >  	ret = tegra_i2c_init(i2c_dev);
> >  	if (ret) {
> >  		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
> > 


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2019-01-26  1:51 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-24 20:51 [PATCH V2 1/4] i2c: tegra: Sort all the include headers alphabetically Sowjanya Komatineni
2019-01-24 20:51 ` Sowjanya Komatineni
2019-01-24 20:51 ` [PATCH V2 2/4] i2c: tegra: Update I2C transfer using buffer Sowjanya Komatineni
2019-01-24 20:51   ` Sowjanya Komatineni
2019-01-25 19:55   ` Dmitry Osipenko
2019-01-25 20:20     ` Sowjanya Komatineni
2019-01-25 21:25       ` Dmitry Osipenko
2019-01-25 23:22         ` Dmitry Osipenko
2019-01-25 23:26           ` Sowjanya Komatineni
2019-01-24 20:51 ` [PATCH V2 3/4] i2c: tegra: Add DMA Support Sowjanya Komatineni
2019-01-24 20:51   ` Sowjanya Komatineni
2019-01-25 19:34   ` Dmitry Osipenko
2019-01-25 20:31     ` Sowjanya Komatineni
2019-01-25 20:40       ` Dmitry Osipenko
2019-01-25 21:11         ` Sowjanya Komatineni
2019-01-25 21:49           ` Dmitry Osipenko
2019-01-25 22:09             ` Dmitry Osipenko
2019-01-25 22:22             ` Sowjanya Komatineni
2019-01-25 23:10           ` Thierry Reding
2019-01-25 23:29             ` Sowjanya Komatineni
2019-01-25 22:03   ` Dmitry Osipenko
2019-01-25 22:35   ` Dmitry Osipenko
2019-01-26  0:28   ` Dmitry Osipenko
2019-01-26  0:49     ` Dmitry Osipenko
2019-01-26  1:51     ` Sowjanya Komatineni
2019-01-24 20:51 ` [PATCH V2 4/4] i2c: tegra: Update transfer timeout Sowjanya Komatineni
2019-01-24 20:51   ` Sowjanya Komatineni
2019-01-24 21:31 ` [PATCH V2 1/4] i2c: tegra: Sort all the include headers alphabetically Dmitry Osipenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.