linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically
@ 2019-02-01 17:07 Sowjanya Komatineni
  2019-02-01 17:07 ` [PATCH V9 2/5] i2c: tegra: add bus clear master support Sowjanya Komatineni
                   ` (4 more replies)
  0 siblings, 5 replies; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 17:07 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>
Acked-by: Thierry Reding <treding@nvidia.com>
---
 [V9] : Rebased to 5.0-rc4
 [V3/V4/V5/V7/V8] : Removed unsued headers in tegra I2C
 [V2] : Added this in V2 to sort the headers in tegra I2C

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

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index c77adbbea0c7..79c6aa87499b 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -6,24 +6,21 @@
  * Author: Colin Cross <ccross@android.com>
  */
 
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.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>
 
 #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
 #define BYTES_PER_FIFO_WORD 4
-- 
2.7.4


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

* [PATCH V9 2/5] i2c: tegra: add bus clear master support
  2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
@ 2019-02-01 17:07 ` Sowjanya Komatineni
  2019-02-01 19:06   ` Dmitry Osipenko
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 17:07 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

Bus clear feature of Tegra I2C controller helps to recover from
bus hang when I2C master loses the bus arbitration due to the
slave device holding SDA LOW continuously for some unknown reasons.

Per I2C specification, the device that held the bus LOW should
release it within 9 clock pulses.

During bus clear operation, Tegra I2C controller sends 9 clock
pulses and terminates the transaction with STOP condition.
Upon successful bus clear operation, bus goes to idle state and
driver retries the transaction.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Acked-by: Thierry Reding <treding@nvidia.com>
---
 [V9] : Rebased to 5.0-rc4
 [V5/V6/V7/V8]: Same as V4
 [V4]: Added I2C Bus Clear support patch to this version of series.

 drivers/i2c/busses/i2c-tegra.c | 73 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 79c6aa87499b..118b7023a0f4 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -51,6 +51,7 @@
 #define I2C_FIFO_STATUS_RX_SHIFT		0
 #define I2C_INT_MASK				0x064
 #define I2C_INT_STATUS				0x068
+#define I2C_INT_BUS_CLR_DONE			BIT(11)
 #define I2C_INT_PACKET_XFER_COMPLETE		BIT(7)
 #define I2C_INT_ALL_PACKETS_XFER_COMPLETE	BIT(6)
 #define I2C_INT_TX_FIFO_OVERFLOW		BIT(5)
@@ -93,6 +94,15 @@
 #define I2C_HEADER_MASTER_ADDR_SHIFT		12
 #define I2C_HEADER_SLAVE_ADDR_SHIFT		1
 
+#define I2C_BUS_CLEAR_CNFG			0x084
+#define I2C_BC_SCLK_THRESHOLD			9
+#define I2C_BC_SCLK_THRESHOLD_SHIFT		16
+#define I2C_BC_STOP_COND			BIT(2)
+#define I2C_BC_TERMINATE			BIT(1)
+#define I2C_BC_ENABLE				BIT(0)
+#define I2C_BUS_CLEAR_STATUS			0x088
+#define I2C_BC_STATUS				BIT(0)
+
 #define I2C_CONFIG_LOAD				0x08C
 #define I2C_MSTR_CONFIG_LOAD			BIT(0)
 #define I2C_SLV_CONFIG_LOAD			BIT(1)
@@ -154,6 +164,8 @@ enum msg_end_type {
  *		be transferred in one go.
  * @quirks: i2c adapter quirks for limiting write/read transfer size and not
  *		allowing 0 length transfers.
+ * @supports_bus_clear: Bus Clear support to recover from bus hang during
+ *		SDA stuck low from device for some unknown reasons.
  */
 struct tegra_i2c_hw_feature {
 	bool has_continue_xfer_support;
@@ -167,6 +179,7 @@ struct tegra_i2c_hw_feature {
 	bool has_slcg_override_reg;
 	bool has_mst_fifo;
 	const struct i2c_adapter_quirks *quirks;
+	bool supports_bus_clear;
 };
 
 /**
@@ -640,6 +653,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 		goto err;
 	}
 
+	/*
+	 * I2C transfer is terminated during the bus clear so skip
+	 * processing the other interrupts.
+	 */
+	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
+		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);
@@ -668,6 +688,8 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	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);
+	if (i2c_dev->hw->supports_bus_clear)
+		tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
 	i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
@@ -678,6 +700,43 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
+{
+	int err;
+	unsigned long time_left;
+	u32 reg;
+
+	if (i2c_dev->hw->supports_bus_clear) {
+		reinit_completion(&i2c_dev->msg_complete);
+		reg = (I2C_BC_SCLK_THRESHOLD << I2C_BC_SCLK_THRESHOLD_SHIFT) |
+		      I2C_BC_STOP_COND | I2C_BC_TERMINATE;
+		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
+		if (i2c_dev->hw->has_config_load_reg) {
+			err = tegra_i2c_wait_for_config_load(i2c_dev);
+			if (err)
+				return err;
+		}
+		reg |= I2C_BC_ENABLE;
+		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
+		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
+
+		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
+							TEGRA_I2C_TIMEOUT);
+		if (time_left == 0) {
+			dev_err(i2c_dev->dev, "timed out for bus clear\n");
+			return -ETIMEDOUT;
+		}
+		reg = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS);
+		if (!(reg & I2C_BC_STATUS)) {
+			dev_err(i2c_dev->dev,
+				"Un-recovered arbitration lost\n");
+			return -EIO;
+		}
+	}
+
+	return -EAGAIN;
+}
+
 static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	struct i2c_msg *msg, enum msg_end_type end_state)
 {
@@ -759,6 +818,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		return 0;
 
 	tegra_i2c_init(i2c_dev);
+	/* start recovery upon arbitration loss in single master mode */
+	if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) {
+		if (!i2c_dev->is_multimaster_mode)
+			return tegra_i2c_issue_bus_clear(i2c_dev);
+		return -EAGAIN;
+	}
+
 	if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
 		if (msg->flags & I2C_M_IGNORE_NAK)
 			return 0;
@@ -853,6 +919,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.has_slcg_override_reg = false,
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -867,6 +934,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.has_slcg_override_reg = false,
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -881,6 +949,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.has_slcg_override_reg = false,
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -895,6 +964,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.has_slcg_override_reg = true,
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -909,6 +979,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.has_slcg_override_reg = true,
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
@@ -923,6 +994,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
 	.has_slcg_override_reg = true,
 	.has_mst_fifo = true,
 	.quirks = &tegra194_i2c_quirks,
+	.supports_bus_clear = true,
 };
 
 /* Match table for of_platform binding */
@@ -974,6 +1046,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	i2c_dev->base = base;
 	i2c_dev->div_clk = div_clk;
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
+	i2c_dev->adapter.retries = 1;
 	i2c_dev->irq = irq;
 	i2c_dev->cont_id = pdev->id;
 	i2c_dev->dev = &pdev->dev;
-- 
2.7.4


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

* [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
  2019-02-01 17:07 ` [PATCH V9 2/5] i2c: tegra: add bus clear master support Sowjanya Komatineni
@ 2019-02-01 17:07 ` Sowjanya Komatineni
  2019-02-01 18:57   ` Dmitry Osipenko
                     ` (4 more replies)
  2019-02-01 17:07 ` [PATCH V9 4/5] i2c: tegra: update transfer timeout Sowjanya Komatineni
                   ` (2 subsequent siblings)
  4 siblings, 5 replies; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 17:07 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. This adds delay between data bytes of the same
transfer when CPU is fully loaded and some slave devices has
internal timeout for no bus activity and stops transaction to
avoid bus hang. DMA mode is helpful in such cases.

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

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 [V9] : Rebased to 5.0-rc4
	Removed dependency of APB DMA in Kconfig and added conditional check
	in I2C driver to decide on using DMA mode.
	Changed back the allocation of dma buffer during i2c probe.
	Fixed FIFO triggers depending on DMA Vs PIO.
 [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
	other fixes
	Updated Kconfig for APB_DMA dependency
 [V7] : Same as V6
 [V6] : Updated for proper buffer allocation/freeing, channel release.
	Updated to use exact xfer size for syncing dma buffer.
 [V5] : Same as V4
 [V4] : Updated to allocate DMA buffer only when DMA mode.
	Updated to fall back to PIO mode when DMA channel request or
	buffer allocation fails.
 [V3] : Updated without additional buffer allocation.
 [V2] : Updated based on V1 review feedback along with code cleanup for
	proper implementation of DMA.

 drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 335 insertions(+), 33 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 118b7023a0f4..ac8009c35863 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -8,6 +8,9 @@
 
 #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>
@@ -44,6 +47,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
@@ -125,6 +130,19 @@
 #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
+
+#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.
@@ -191,6 +209,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
+ * @base_phys: Physical base address of the I2C controller
  * @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
@@ -204,6 +223,13 @@ 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
+ * @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;
@@ -213,6 +239,7 @@ struct tegra_i2c_dev {
 	struct clk *fast_clk;
 	struct reset_control *rst;
 	void __iomem *base;
+	phys_addr_t base_phys;
 	int cont_id;
 	int irq;
 	bool irq_disabled;
@@ -226,6 +253,13 @@ struct tegra_i2c_dev {
 	u16 clk_divisor_non_hs_mode;
 	bool is_multimaster_mode;
 	spinlock_t xfer_lock;
+	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 void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
@@ -294,6 +328,86 @@ 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;
+	struct dma_chan *chan;
+
+	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;
+	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
+	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(struct tegra_i2c_dev *i2c_dev)
+{
+	struct dma_chan *dma_chan;
+	u32 *dma_buf;
+	dma_addr_t dma_phys;
+	int err = 0;
+
+	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
+		return -ENODEV;
+
+	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
+	if (IS_ERR(dma_chan))
+		return PTR_ERR(dma_chan);
+
+	i2c_dev->rx_dma_chan = dma_chan;
+
+	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
+	if (IS_ERR(dma_chan)) {
+		err = PTR_ERR(dma_chan);
+		goto error;
+	}
+
+	i2c_dev->tx_dma_chan = dma_chan;
+
+	dma_buf = dma_alloc_coherent(i2c_dev->dev,
+				     i2c_dev->dma_buf_size, &dma_phys,
+				     GFP_KERNEL | __GFP_NOWARN);
+
+	if (!dma_buf) {
+		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
+		err = -ENOMEM;
+		goto error;
+	}
+
+	i2c_dev->dma_buf = dma_buf;
+	i2c_dev->dma_phys = dma_phys;
+	return 0;
+
+error:
+	if (i2c_dev->tx_dma_chan)
+		dma_release_channel(i2c_dev->tx_dma_chan);
+
+	if (i2c_dev->rx_dma_chan)
+		dma_release_channel(i2c_dev->rx_dma_chan);
+
+	return err;
+}
+
 static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -571,16 +685,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
 	}
 
-	if (i2c_dev->hw->has_mst_fifo) {
-		val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
-		      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
-		i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
-	} else {
-		val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
-			0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
-		i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
-	}
-
 	err = tegra_i2c_flush_fifos(i2c_dev);
 	if (err)
 		goto err;
@@ -660,25 +764,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
 		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);
 
+	/*
+	 * During message read XFER_COMPLETE interrupt is triggered prior to
+	 * DMA completion and during message write XFER_COMPLETE interrupt is
+	 * triggered after DMA completion.
+	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
+	 * so forcing msg_buf_remaining to 0 in DMA mode.
+	 */
 	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);
 	}
@@ -694,12 +810,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
 	if (i2c_dev->is_dvc)
 		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
+	if (i2c_dev->is_curr_dma_xfer) {
+		if (i2c_dev->msg_read)
+			dmaengine_terminate_all(i2c_dev->rx_dma_chan);
+		else
+			dmaengine_terminate_all(i2c_dev->tx_dma_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;
+	struct dma_chan *chan;
+
+	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->base_phys + 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->base_phys + I2C_RX_FIFO;
+		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_sconfig.src_maxburst = dma_burst;
+	}
+
+	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
+	dmaengine_slave_config(chan, &dma_sconfig);
+}
+
 static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
 {
 	int err;
@@ -741,9 +914,13 @@ 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;
+	u32 int_mask, val;
 	unsigned long time_left;
 	unsigned long flags;
+	size_t xfer_size;
+	u32 *buffer = NULL;
+	int err = 0;
+	bool dma = false;
 
 	tegra_i2c_flush_fifos(i2c_dev);
 
@@ -753,19 +930,72 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	i2c_dev->msg_read = (msg->flags & I2C_M_RD);
 	reinit_completion(&i2c_dev->msg_complete);
 
+	if (i2c_dev->msg_read)
+		xfer_size = msg->len;
+	else
+		xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
+
+	xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
+
+	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;
+
 	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 (dma) {
+		if (i2c_dev->msg_read) {
+			tegra_i2c_config_fifo_trig(i2c_dev, xfer_size,
+						   DATA_DMA_DIR_RX);
+			dma_sync_single_for_device(i2c_dev->dev,
+						   i2c_dev->dma_phys,
+						   xfer_size,
+						   DMA_FROM_DEVICE);
+			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+			if (err < 0) {
+				dev_err(i2c_dev->dev,
+					"starting RX DMA failed, err %d\n",
+					err);
+				goto unlock;
+			}
+		} else {
+			tegra_i2c_config_fifo_trig(i2c_dev, xfer_size,
+						   DATA_DMA_DIR_TX);
+			dma_sync_single_for_cpu(i2c_dev->dev,
+						i2c_dev->dma_phys,
+						xfer_size,
+						DMA_TO_DEVICE);
+			buffer = i2c_dev->dma_buf;
+		}
+	} else {
+		if (i2c_dev->hw->has_mst_fifo) {
+			val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
+			      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
+			i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
+		} else {
+			val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
+				0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
+			i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+		}
+	}
 
 	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 && !i2c_dev->msg_read)
+		*buffer++ = packet_header;
+	else
+		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
 
 	packet_header = msg->len - 1;
-	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+	if (dma && !i2c_dev->msg_read)
+		*buffer++ = packet_header;
+	else
+		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
 
 	packet_header = I2C_HEADER_IE_ENABLE;
 	if (end_state == MSG_END_CONTINUE)
@@ -782,23 +1012,77 @@ 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);
-
-	if (!(msg->flags & I2C_M_RD))
-		tegra_i2c_fill_tx_fifo(i2c_dev);
+	if (dma && !i2c_dev->msg_read)
+		*buffer++ = packet_header;
+	else
+		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+	if (!msg->flags & I2C_M_RD) {
+		if (dma) {
+			memcpy(buffer, msg->buf, msg->len);
+			dma_sync_single_for_device(i2c_dev->dev,
+						   i2c_dev->dma_phys,
+						   xfer_size,
+						   DMA_TO_DEVICE);
+			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+			if (err < 0) {
+				dev_err(i2c_dev->dev,
+					"starting TX DMA failed, err %d\n",
+					err);
+				goto unlock;
+			}
+		} else {
+			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;
+
+	if (!dma) {
+		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;
+	}
 
 	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));
 
+unlock:
+	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+
+	if (dma) {
+		if (err)
+			return err;
+
+		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(i2c_dev->msg_read ?
+						i2c_dev->rx_dma_chan :
+						i2c_dev->tx_dma_chan);
+			tegra_i2c_init(i2c_dev);
+			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,
+							xfer_size,
+							DMA_FROM_DEVICE);
+
+				memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf,
+					msg->len);
+			}
+		}
+	}
+
 	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
 						TEGRA_I2C_TIMEOUT);
 	tegra_i2c_mask_irq(i2c_dev, int_mask);
@@ -1017,11 +1301,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	struct clk *div_clk;
 	struct clk *fast_clk;
 	void __iomem *base;
+	phys_addr_t base_phys;
 	int irq;
 	int ret = 0;
 	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base_phys = res->start;
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
@@ -1044,6 +1330,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	i2c_dev->base = base;
+	i2c_dev->base_phys = base_phys;
 	i2c_dev->div_clk = div_clk;
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
 	i2c_dev->adapter.retries = 1;
@@ -1063,7 +1350,9 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
 						  "nvidia,tegra20-i2c-dvc");
 	i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
+	i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len;
 	init_completion(&i2c_dev->msg_complete);
+	init_completion(&i2c_dev->dma_complete);
 	spin_lock_init(&i2c_dev->xfer_lock);
 
 	if (!i2c_dev->hw->has_single_clk_source) {
@@ -1124,6 +1413,10 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		}
 	}
 
+	ret = tegra_i2c_init_dma(i2c_dev);
+	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");
@@ -1188,6 +1481,15 @@ static int tegra_i2c_remove(struct platform_device *pdev)
 	if (!i2c_dev->hw->has_single_clk_source)
 		clk_unprepare(i2c_dev->fast_clk);
 
+	if (i2c_dev->dma_buf)
+		dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+				  i2c_dev->dma_buf, i2c_dev->dma_phys);
+	if (i2c_dev->tx_dma_chan)
+		dma_release_channel(i2c_dev->tx_dma_chan);
+
+	if (i2c_dev->tx_dma_chan)
+		dma_release_channel(i2c_dev->rx_dma_chan);
+
 	return 0;
 }
 
-- 
2.7.4


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

* [PATCH V9 4/5] i2c: tegra: update transfer timeout
  2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
  2019-02-01 17:07 ` [PATCH V9 2/5] i2c: tegra: add bus clear master support Sowjanya Komatineni
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
@ 2019-02-01 17:07 ` Sowjanya Komatineni
  2019-02-01 19:16   ` Dmitry Osipenko
  2019-02-01 17:07 ` [PATCH V9 5/5] i2c: tegra: add i2c interface timing support Sowjanya Komatineni
  2019-02-01 19:05 ` [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Dmitry Osipenko
  4 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 17:07 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

Tegra194 allows max of 64K bytes and Tegra186 and prior allows
max of 4K bytes of transfer per packet.

one sec timeout is not enough for transfers more than 10K bytes
at STD bus rate.

This patch updates I2C transfer timeout based on the transfer size
and I2C bus rate to allow enough time during max transfer size at
lower bus speed.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Acked-by: Thierry Reding <treding@nvidia.com>
---
 [V9] : Rebased to 5.0-rc4
	Minor updates for readability of xfer time
 [V8] : Added comment with explaination of xfer time calculation
 [V5/V6/V7] : Same as V4
 [V4] : V4 series includes bus clear support and this patch is updated with
	fixed timeout of 1sec for bus clear operation.
 [V3] : Same as V2
 [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 | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index ac8009c35863..43449495bfaa 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -25,7 +25,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
 #define BYTES_PER_FIFO_WORD 4
 
 #define I2C_CNFG				0x000
@@ -893,8 +892,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
 		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
 		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
 
-		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
-							TEGRA_I2C_TIMEOUT);
+		time_left = wait_for_completion_timeout(
+						&i2c_dev->msg_complete,
+						msecs_to_jiffies(1000));
 		if (time_left == 0) {
 			dev_err(i2c_dev->dev, "timed out for bus clear\n");
 			return -ETIMEDOUT;
@@ -921,6 +921,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	u32 *buffer = NULL;
 	int err = 0;
 	bool dma = false;
+	u16 xfer_time = 100;
 
 	tegra_i2c_flush_fifos(i2c_dev);
 
@@ -942,6 +943,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 
 	i2c_dev->is_curr_dma_xfer = dma;
 
+	/*
+	 * Transfer time in mSec = Total bits / transfer rate
+	 * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
+	 */
+	xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
+					i2c_dev->bus_clk_rate);
+
 	spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
 
 	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
@@ -1059,7 +1067,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 
 		time_left = wait_for_completion_timeout(
 						&i2c_dev->dma_complete,
-						TEGRA_I2C_TIMEOUT);
+						msecs_to_jiffies(xfer_time));
 
 		if (time_left == 0) {
 			dev_err(i2c_dev->dev, "DMA transfer timeout\n");
@@ -1083,8 +1091,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 		}
 	}
 
-	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
-						TEGRA_I2C_TIMEOUT);
+	time_left = wait_for_completion_timeout(
+					&i2c_dev->msg_complete,
+					msecs_to_jiffies(xfer_time));
 	tegra_i2c_mask_irq(i2c_dev, int_mask);
 
 	if (time_left == 0) {
-- 
2.7.4


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

* [PATCH V9 5/5] i2c: tegra: add i2c interface timing support
  2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
                   ` (2 preceding siblings ...)
  2019-02-01 17:07 ` [PATCH V9 4/5] i2c: tegra: update transfer timeout Sowjanya Komatineni
@ 2019-02-01 17:07 ` Sowjanya Komatineni
  2019-02-01 19:05 ` [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Dmitry Osipenko
  4 siblings, 0 replies; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 17:07 UTC (permalink / raw)
  To: thierry.reding, jonathanh, mkarthik, smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c, Sowjanya Komatineni

This patch adds I2C interface timing registers support for
proper bus rate configuration along with meeting the i2c spec
setup and hold times based on the tuning performed on Tegra210,
Tegra186 and Tegra194 platforms.

I2C_INTERFACE_TIMING_0 register contains TLOW and THIGH field
and Tegra I2C controller design uses them as a part of internal
clock divisor.

I2C_INTERFACE_TIMING_1 register contains the setup and hold times
for start and stop conditions.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Acked-by: Thierry Reding <treding@nvidia.com>
---
 [V9] : Rebased to 5.0-rc4
	Minor update to commit message
 [V8] : Updated to handle timing implementation within tegra_i2c_init directly
 [V7] : Minor updates to timing implementation
 [V5/V6] : Added this Interface timing patch in V5 of the patch series.

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

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 43449495bfaa..32d5744bce45 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -129,6 +129,15 @@
 #define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
 #define I2C_MST_FIFO_STATUS_TX_SHIFT		16
 
+#define I2C_INTERFACE_TIMING_0			0x94
+#define I2C_THIGH_SHIFT				8
+#define I2C_INTERFACE_TIMING_1			0x98
+
+#define I2C_STANDARD_MODE			100000
+#define I2C_FAST_MODE				400000
+#define I2C_FAST_PLUS_MODE			1000000
+#define I2C_HS_MODE				3500000
+
 /* Packet header size in bytes */
 #define I2C_PACKET_HEADER_SIZE			12
 
@@ -166,7 +175,10 @@ enum msg_end_type {
  * @has_config_load_reg: Has the config load register to load the new
  *		configuration.
  * @clk_divisor_hs_mode: Clock divisor in HS mode.
- * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
+ * @clk_divisor_std_mode: Clock divisor in standard mode. It is
+ *		applicable if there is no fast clock source i.e. single clock
+ *		source.
+ * @clk_divisor_fast_mode: Clock divisor in fast mode. It is
  *		applicable if there is no fast clock source i.e. single clock
  *		source.
  * @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is
@@ -183,6 +195,16 @@ enum msg_end_type {
  *		allowing 0 length transfers.
  * @supports_bus_clear: Bus Clear support to recover from bus hang during
  *		SDA stuck low from device for some unknown reasons.
+ * @tlow_std_mode: Low period of the clock in standard mode.
+ * @thigh_std_mode: High period of the clock in standard mode.
+ * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes.
+ * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes.
+ * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions
+ *		in standard mode.
+ * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop
+ *		conditions in fast/fast-plus modes.
+ * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions
+ *		in HS mode.
  */
 struct tegra_i2c_hw_feature {
 	bool has_continue_xfer_support;
@@ -190,13 +212,21 @@ struct tegra_i2c_hw_feature {
 	bool has_single_clk_source;
 	bool has_config_load_reg;
 	int clk_divisor_hs_mode;
-	int clk_divisor_std_fast_mode;
+	int clk_divisor_std_mode;
+	int clk_divisor_fast_mode;
 	u16 clk_divisor_fast_plus_mode;
 	bool has_multi_master_mode;
 	bool has_slcg_override_reg;
 	bool has_mst_fifo;
 	const struct i2c_adapter_quirks *quirks;
 	bool supports_bus_clear;
+	u8 tlow_std_mode;
+	u8 thigh_std_mode;
+	u8 tlow_fast_fastplus_mode;
+	u8 thigh_fast_fastplus_mode;
+	u32 setup_hold_time_std_mode;
+	u32 setup_hold_time_fast_fast_plus_mode;
+	u32 setup_hold_time_hs_mode;
 };
 
 /**
@@ -641,11 +671,13 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
 	return 0;
 }
 
-static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit)
 {
 	u32 val;
 	int err;
-	u32 clk_divisor;
+	u32 clk_divisor, clk_multiplier;
+	u32 tsu_thd = 0;
+	u8 tlow, thigh;
 
 	err = pm_runtime_get_sync(i2c_dev->dev);
 	if (err < 0) {
@@ -675,6 +707,36 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 					I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
 	i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
 
+	if ((i2c_dev->bus_clk_rate > I2C_STANDARD_MODE) &&
+	    (i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE)) {
+		tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
+		thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
+		tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
+	} else {
+		tlow = i2c_dev->hw->tlow_std_mode;
+		thigh = i2c_dev->hw->thigh_std_mode;
+		tsu_thd = i2c_dev->hw->setup_hold_time_std_mode;
+	}
+
+	val = (thigh << I2C_THIGH_SHIFT) | tlow;
+	if (val)
+		i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0);
+
+	if (tsu_thd)
+		i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1);
+
+	if (!clk_reinit) {
+		clk_multiplier = (tlow + thigh + 2);
+		clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
+		err = clk_set_rate(i2c_dev->div_clk,
+				   i2c_dev->bus_clk_rate * clk_multiplier);
+		if (err) {
+			dev_err(i2c_dev->dev,
+				"failed changing clock rate: %d\n", err);
+			goto err;
+		}
+	}
+
 	if (!i2c_dev->is_dvc) {
 		u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
 
@@ -1074,7 +1136,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 			dmaengine_terminate_all(i2c_dev->msg_read ?
 						i2c_dev->rx_dma_chan :
 						i2c_dev->tx_dma_chan);
-			tegra_i2c_init(i2c_dev);
+			tegra_i2c_init(i2c_dev, true);
 			return -ETIMEDOUT;
 		}
 
@@ -1099,7 +1161,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (time_left == 0) {
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 
-		tegra_i2c_init(i2c_dev);
+		tegra_i2c_init(i2c_dev, true);
 		return -ETIMEDOUT;
 	}
 
@@ -1110,7 +1172,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 	if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
 		return 0;
 
-	tegra_i2c_init(i2c_dev);
+	tegra_i2c_init(i2c_dev, true);
 	/* start recovery upon arbitration loss in single master mode */
 	if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) {
 		if (!i2c_dev->is_multimaster_mode)
@@ -1205,7 +1267,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.has_per_pkt_xfer_complete_irq = false,
 	.has_single_clk_source = false,
 	.clk_divisor_hs_mode = 3,
-	.clk_divisor_std_fast_mode = 0,
+	.clk_divisor_std_mode = 0,
+	.clk_divisor_fast_mode = 0,
 	.clk_divisor_fast_plus_mode = 0,
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
@@ -1213,6 +1276,13 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
 	.supports_bus_clear = false,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x2,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0x0,
+	.setup_hold_time_fast_fast_plus_mode = 0x0,
+	.setup_hold_time_hs_mode = 0x0,
 };
 
 static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -1220,7 +1290,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.has_per_pkt_xfer_complete_irq = false,
 	.has_single_clk_source = false,
 	.clk_divisor_hs_mode = 3,
-	.clk_divisor_std_fast_mode = 0,
+	.clk_divisor_std_mode = 0,
+	.clk_divisor_fast_mode = 0,
 	.clk_divisor_fast_plus_mode = 0,
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
@@ -1228,6 +1299,13 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
 	.supports_bus_clear = false,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x2,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0x0,
+	.setup_hold_time_fast_fast_plus_mode = 0x0,
+	.setup_hold_time_hs_mode = 0x0,
 };
 
 static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -1235,7 +1313,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.has_per_pkt_xfer_complete_irq = true,
 	.has_single_clk_source = true,
 	.clk_divisor_hs_mode = 1,
-	.clk_divisor_std_fast_mode = 0x19,
+	.clk_divisor_std_mode = 0x19,
+	.clk_divisor_fast_mode = 0x19,
 	.clk_divisor_fast_plus_mode = 0x10,
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
@@ -1243,6 +1322,13 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
 	.supports_bus_clear = true,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x2,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0x0,
+	.setup_hold_time_fast_fast_plus_mode = 0x0,
+	.setup_hold_time_hs_mode = 0x0,
 };
 
 static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -1250,7 +1336,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.has_per_pkt_xfer_complete_irq = true,
 	.has_single_clk_source = true,
 	.clk_divisor_hs_mode = 1,
-	.clk_divisor_std_fast_mode = 0x19,
+	.clk_divisor_std_mode = 0x19,
+	.clk_divisor_fast_mode = 0x19,
 	.clk_divisor_fast_plus_mode = 0x10,
 	.has_config_load_reg = true,
 	.has_multi_master_mode = false,
@@ -1258,6 +1345,13 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
 	.supports_bus_clear = true,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x2,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0x0,
+	.setup_hold_time_fast_fast_plus_mode = 0x0,
+	.setup_hold_time_hs_mode = 0x0,
 };
 
 static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -1265,7 +1359,8 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.has_per_pkt_xfer_complete_irq = true,
 	.has_single_clk_source = true,
 	.clk_divisor_hs_mode = 1,
-	.clk_divisor_std_fast_mode = 0x19,
+	.clk_divisor_std_mode = 0x19,
+	.clk_divisor_fast_mode = 0x19,
 	.clk_divisor_fast_plus_mode = 0x10,
 	.has_config_load_reg = true,
 	.has_multi_master_mode = true,
@@ -1273,26 +1368,65 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.has_mst_fifo = false,
 	.quirks = &tegra_i2c_quirks,
 	.supports_bus_clear = true,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x2,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0,
+	.setup_hold_time_fast_fast_plus_mode = 0,
+	.setup_hold_time_hs_mode = 0,
 };
 
-static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
 	.has_continue_xfer_support = true,
 	.has_per_pkt_xfer_complete_irq = true,
 	.has_single_clk_source = true,
 	.clk_divisor_hs_mode = 1,
-	.clk_divisor_std_fast_mode = 0x19,
+	.clk_divisor_std_mode = 0x16,
+	.clk_divisor_fast_mode = 0x19,
 	.clk_divisor_fast_plus_mode = 0x10,
 	.has_config_load_reg = true,
 	.has_multi_master_mode = true,
 	.has_slcg_override_reg = true,
 	.has_mst_fifo = true,
+	.quirks = &tegra_i2c_quirks,
+	.supports_bus_clear = true,
+	.tlow_std_mode = 0x4,
+	.thigh_std_mode = 0x3,
+	.tlow_fast_fastplus_mode = 0x4,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0,
+	.setup_hold_time_fast_fast_plus_mode = 0,
+	.setup_hold_time_hs_mode = 0,
+};
+
+static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+	.has_continue_xfer_support = true,
+	.has_per_pkt_xfer_complete_irq = true,
+	.has_single_clk_source = true,
+	.clk_divisor_hs_mode = 1,
+	.clk_divisor_std_mode = 0x4f,
+	.clk_divisor_fast_mode = 0x3c,
+	.clk_divisor_fast_plus_mode = 0x16,
+	.has_config_load_reg = true,
+	.has_multi_master_mode = true,
+	.has_slcg_override_reg = true,
+	.has_mst_fifo = true,
 	.quirks = &tegra194_i2c_quirks,
 	.supports_bus_clear = true,
+	.tlow_std_mode = 0x8,
+	.thigh_std_mode = 0x7,
+	.tlow_fast_fastplus_mode = 0x2,
+	.thigh_fast_fastplus_mode = 0x2,
+	.setup_hold_time_std_mode = 0x08080808,
+	.setup_hold_time_fast_fast_plus_mode = 0x02020202,
+	.setup_hold_time_hs_mode = 0x090909,
 };
 
 /* Match table for of_platform binding */
 static const struct of_device_id tegra_i2c_of_match[] = {
 	{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
+	{ .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
 	{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
 	{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
 	{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
@@ -1313,7 +1447,6 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	phys_addr_t base_phys;
 	int irq;
 	int ret = 0;
-	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	base_phys = res->start;
@@ -1383,20 +1516,17 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 		}
 	}
 
-	i2c_dev->clk_divisor_non_hs_mode =
-			i2c_dev->hw->clk_divisor_std_fast_mode;
-	if (i2c_dev->hw->clk_divisor_fast_plus_mode &&
-		(i2c_dev->bus_clk_rate == 1000000))
+	if ((i2c_dev->bus_clk_rate > I2C_FAST_MODE) &&
+	    (i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE))
 		i2c_dev->clk_divisor_non_hs_mode =
-			i2c_dev->hw->clk_divisor_fast_plus_mode;
-
-	clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
-	ret = clk_set_rate(i2c_dev->div_clk,
-			   i2c_dev->bus_clk_rate * clk_multiplier);
-	if (ret) {
-		dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret);
-		goto unprepare_fast_clk;
-	}
+				i2c_dev->hw->clk_divisor_fast_plus_mode;
+	else if ((i2c_dev->bus_clk_rate > I2C_STANDARD_MODE) &&
+		 (i2c_dev->bus_clk_rate <= I2C_FAST_MODE))
+		i2c_dev->clk_divisor_non_hs_mode =
+				i2c_dev->hw->clk_divisor_fast_mode;
+	else
+		i2c_dev->clk_divisor_non_hs_mode =
+				i2c_dev->hw->clk_divisor_std_mode;
 
 	ret = clk_prepare(i2c_dev->div_clk);
 	if (ret < 0) {
@@ -1426,7 +1556,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	if (ret == -EPROBE_DEFER)
 		goto disable_div_clk;
 
-	ret = tegra_i2c_init(i2c_dev);
+	ret = tegra_i2c_init(i2c_dev, false);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
 		goto disable_div_clk;
-- 
2.7.4


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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
@ 2019-02-01 18:57   ` Dmitry Osipenko
  2019-02-01 19:20     ` Sowjanya Komatineni
  2019-02-01 20:21     ` Sowjanya Komatineni
  2019-02-02 14:01   ` Dmitry Osipenko
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 18:57 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, 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. This adds delay between data bytes of the same
> transfer when CPU is fully loaded and some slave devices has
> internal timeout for no bus activity and stops transaction to
> avoid bus hang. DMA mode is helpful in such cases.
> 
> DMA mode is also helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
> 	Removed dependency of APB DMA in Kconfig and added conditional check
> 	in I2C driver to decide on using DMA mode.
> 	Changed back the allocation of dma buffer during i2c probe.
> 	Fixed FIFO triggers depending on DMA Vs PIO.
>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
> 	other fixes
> 	Updated Kconfig for APB_DMA dependency
>  [V7] : Same as V6
>  [V6] : Updated for proper buffer allocation/freeing, channel release.
> 	Updated to use exact xfer size for syncing dma buffer.
>  [V5] : Same as V4
>  [V4] : Updated to allocate DMA buffer only when DMA mode.
> 	Updated to fall back to PIO mode when DMA channel request or
> 	buffer allocation fails.
>  [V3] : Updated without additional buffer allocation.
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 335 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 118b7023a0f4..ac8009c35863 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -8,6 +8,9 @@
>  
>  #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>
> @@ -44,6 +47,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
> @@ -125,6 +130,19 @@
>  #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
> +
> +#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.
> @@ -191,6 +209,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
> + * @base_phys: Physical base address of the I2C controller
>   * @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
> @@ -204,6 +223,13 @@ 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
> + * @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;
> @@ -213,6 +239,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t base_phys;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -226,6 +253,13 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	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 void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
> @@ -294,6 +328,86 @@ 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;
> +	struct dma_chan *chan;
> +
> +	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;
> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
> +	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(struct tegra_i2c_dev *i2c_dev)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int err = 0;
> +
> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> +		return -ENODEV;

Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.

Should be:

	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
		return 0;

> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);
> +
> +	i2c_dev->rx_dma_chan = dma_chan;

It's usually better to postpone assignment of the values until all of relevant resources are allocated.

Hence:

	struct dma_chan *rx_chan, *tx_chan;

...

	rx_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
	if (IS_ERR(rx_chan))
		return PTR_ERR(rx_chan);


> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
> +	if (IS_ERR(dma_chan)) {
> +		err = PTR_ERR(dma_chan);
> +		goto error;

It's a good practice to release resources in opposite order to the allocation. Hence better to write this as:

		goto err_release_rx;

> +	}
> +
> +	i2c_dev->tx_dma_chan = dma_chan;
> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
> +				     i2c_dev->dma_buf_size, &dma_phys,
> +				     GFP_KERNEL | __GFP_NOWARN);
> +
> +	if (!dma_buf) {
> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
> +		err = -ENOMEM;
> +		goto error;

		goto err_release_tx;

> +	}
> +
> +	i2c_dev->dma_buf = dma_buf;
> +	i2c_dev->dma_phys = dma_phys;	i2c_dev->rx_dma_chan = rx_chan;
	i2c_dev->tx_dma_chan = tx_chan;

> +	return 0;
> +
> +error:
> +	if (i2c_dev->tx_dma_chan)
> +		dma_release_channel(i2c_dev->tx_dma_chan);
> +
> +	if (i2c_dev->rx_dma_chan)
> +		dma_release_channel(i2c_dev->rx_dma_chan);

error_release_tx:
	dma_release_channel(tx_chan);
error_release_rx:
	dma_release_channel(rx_chan);

> +
> +	return err;
> +}
> +
>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>  {
>  	unsigned long timeout = jiffies + HZ;
> @@ -571,16 +685,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
>  	}
>  
> -	if (i2c_dev->hw->has_mst_fifo) {
> -		val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
> -		      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
> -		i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
> -	} else {
> -		val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
> -			0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
> -		i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
> -	}
> -
>  	err = tegra_i2c_flush_fifos(i2c_dev);
>  	if (err)
>  		goto err;
> @@ -660,25 +764,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
>  		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);
>  
> +	/*
> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
> +	 * DMA completion and during message write XFER_COMPLETE interrupt is
> +	 * triggered after DMA completion.
> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
> +	 * so forcing msg_buf_remaining to 0 in DMA mode.
> +	 */
>  	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);
>  	}
> @@ -694,12 +810,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> +	if (i2c_dev->is_curr_dma_xfer) {
> +		if (i2c_dev->msg_read)
> +			dmaengine_terminate_all(i2c_dev->rx_dma_chan);
> +		else
> +			dmaengine_terminate_all(i2c_dev->tx_dma_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;
> +	struct dma_chan *chan;
> +
> +	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->base_phys + 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->base_phys + I2C_RX_FIFO;
> +		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_sconfig.src_maxburst = dma_burst;
> +	}
> +
> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
> +	dmaengine_slave_config(chan, &dma_sconfig);
> +}
> +
>  static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
>  {
>  	int err;
> @@ -741,9 +914,13 @@ 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;
> +	u32 int_mask, val;
>  	unsigned long time_left;
>  	unsigned long flags;
> +	size_t xfer_size;
> +	u32 *buffer = NULL;
> +	int err = 0;
> +	bool dma = false;
>  
>  	tegra_i2c_flush_fifos(i2c_dev);
>  
> @@ -753,19 +930,72 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
>  	i2c_dev->msg_read = (msg->flags & I2C_M_RD);
>  	reinit_completion(&i2c_dev->msg_complete);
>  
> +	if (i2c_dev->msg_read)
> +		xfer_size = msg->len;
> +	else
> +		xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
> +
> +	xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
> +
> +	dma = (xfer_size > I2C_PIO_MODE_MAX_LEN) &&
> +	      i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan;

Will be shorter to write:

	dma = (xfer_size > I2C_PIO_MODE_MAX_LEN) && i2c_dev->dma_buf;

> +
> +	i2c_dev->is_curr_dma_xfer = dma;
> +
>  	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 (dma) {
> +		if (i2c_dev->msg_read) {
> +			tegra_i2c_config_fifo_trig(i2c_dev, xfer_size,
> +						   DATA_DMA_DIR_RX);
> +			dma_sync_single_for_device(i2c_dev->dev,
> +						   i2c_dev->dma_phys,
> +						   xfer_size,
> +						   DMA_FROM_DEVICE);
> +			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
> +			if (err < 0) {
> +				dev_err(i2c_dev->dev,
> +					"starting RX DMA failed, err %d\n",
> +					err);
> +				goto unlock;
> +			}
> +		} else {
> +			tegra_i2c_config_fifo_trig(i2c_dev, xfer_size,
> +						   DATA_DMA_DIR_TX);
> +			dma_sync_single_for_cpu(i2c_dev->dev,
> +						i2c_dev->dma_phys,
> +						xfer_size,
> +						DMA_TO_DEVICE);
> +			buffer = i2c_dev->dma_buf;
> +		}
> +	} else {
> +		if (i2c_dev->hw->has_mst_fifo) {
> +			val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
> +			      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
> +			i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
> +		} else {
> +			val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
> +				0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
> +			i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
> +		}
> +	}
>  
>  	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 && !i2c_dev->msg_read)
> +		*buffer++ = packet_header;
> +	else
> +		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
>  
>  	packet_header = msg->len - 1;
> -	i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> +	if (dma && !i2c_dev->msg_read)
> +		*buffer++ = packet_header;
> +	else
> +		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
>  
>  	packet_header = I2C_HEADER_IE_ENABLE;
>  	if (end_state == MSG_END_CONTINUE)
> @@ -782,23 +1012,77 @@ 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);
> -
> -	if (!(msg->flags & I2C_M_RD))
> -		tegra_i2c_fill_tx_fifo(i2c_dev);
> +	if (dma && !i2c_dev->msg_read)
> +		*buffer++ = packet_header;
> +	else
> +		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
> +
> +	if (!msg->flags & I2C_M_RD) {
> +		if (dma) {
> +			memcpy(buffer, msg->buf, msg->len);
> +			dma_sync_single_for_device(i2c_dev->dev,
> +						   i2c_dev->dma_phys,
> +						   xfer_size,
> +						   DMA_TO_DEVICE);
> +			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
> +			if (err < 0) {
> +				dev_err(i2c_dev->dev,
> +					"starting TX DMA failed, err %d\n",
> +					err);
> +				goto unlock;
> +			}
> +		} else {
> +			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;
> +
> +	if (!dma) {
> +		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;
> +	}
>  
>  	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));
>  
> +unlock:
> +	spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
> +
> +	if (dma) {
> +		if (err)
> +			return err;
> +
> +		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(i2c_dev->msg_read ?
> +						i2c_dev->rx_dma_chan :
> +						i2c_dev->tx_dma_chan);
> +			tegra_i2c_init(i2c_dev);
> +			return -ETIMEDOUT;
> +		}
> +
> +		if (i2c_dev->msg_read) {
> +			if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) {

likely() isn't really needed here, it's not a hotpath. Write this as:

		if (i2c_dev->msg_read &&
		    i2c_dev->msg_err == I2C_ERR_NONE)) {


> +				dma_sync_single_for_cpu(i2c_dev->dev,
> +							i2c_dev->dma_phys,
> +							xfer_size,
> +							DMA_FROM_DEVICE);
> +
> +				memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf,
> +					msg->len);
> +			}
> +		}
> +	}
> +
>  	time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
>  						TEGRA_I2C_TIMEOUT);
>  	tegra_i2c_mask_irq(i2c_dev, int_mask);
> @@ -1017,11 +1301,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  	struct clk *div_clk;
>  	struct clk *fast_clk;
>  	void __iomem *base;
> +	phys_addr_t base_phys;
>  	int irq;
>  	int ret = 0;
>  	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
>  
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base_phys = res->start;
>  	base = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(base))
>  		return PTR_ERR(base);
> @@ -1044,6 +1330,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	i2c_dev->base = base;
> +	i2c_dev->base_phys = base_phys;
>  	i2c_dev->div_clk = div_clk;
>  	i2c_dev->adapter.algo = &tegra_i2c_algo;
>  	i2c_dev->adapter.retries = 1;
> @@ -1063,7 +1350,9 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  	i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
>  						  "nvidia,tegra20-i2c-dvc");
>  	i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
> +	i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len;
>  	init_completion(&i2c_dev->msg_complete);
> +	init_completion(&i2c_dev->dma_complete);
>  	spin_lock_init(&i2c_dev->xfer_lock);
>  
>  	if (!i2c_dev->hw->has_single_clk_source) {
> @@ -1124,6 +1413,10 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> +	ret = tegra_i2c_init_dma(i2c_dev);
> +	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");
> @@ -1188,6 +1481,15 @@ static int tegra_i2c_remove(struct platform_device *pdev)
>  	if (!i2c_dev->hw->has_single_clk_source)
>  		clk_unprepare(i2c_dev->fast_clk);
>  
> +	if (i2c_dev->dma_buf)
> +		dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
> +				  i2c_dev->dma_buf, i2c_dev->dma_phys);
> +	if (i2c_dev->tx_dma_chan)
> +		dma_release_channel(i2c_dev->tx_dma_chan);
> +
> +	if (i2c_dev->tx_dma_chan)
> +		dma_release_channel(i2c_dev->rx_dma_chan);
> +
>  	return 0;
>  }
>  
> 

I'll test patches during next days. In general this series looks quite good now.

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

* Re: [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically
  2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
                   ` (3 preceding siblings ...)
  2019-02-01 17:07 ` [PATCH V9 5/5] i2c: tegra: add i2c interface timing support Sowjanya Komatineni
@ 2019-02-01 19:05 ` Dmitry Osipenko
  4 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 19:05 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, Sowjanya Komatineni пишет:
> This patch sorts all the include headers alphabetically for the
> I2C Tegra driver.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
>  [V3/V4/V5/V7/V8] : Removed unsued headers in tegra I2C
>  [V2] : Added this in V2 to sort the headers in tegra I2C
> 
>  drivers/i2c/busses/i2c-tegra.c | 19 ++++++++-----------
>  1 file changed, 8 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index c77adbbea0c7..79c6aa87499b 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -6,24 +6,21 @@
>   * Author: Colin Cross <ccross@android.com>
>   */
>  
> -#include <linux/kernel.h>
> -#include <linux/init.h>
> -#include <linux/platform_device.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>
>  
>  #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
>  #define BYTES_PER_FIFO_WORD 4
> 

I gave couple R-B's in v8, please don't shy to add them to the patches.

Also a small hint for the future: if somebody makes a review of your patches and you want that person to take a look at the next versions, you should add that person to email's CC because sometime patches are getting delayed by mailing list for a very substantial time (days) or even getting lost. 


Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* Re: [PATCH V9 2/5] i2c: tegra: add bus clear master support
  2019-02-01 17:07 ` [PATCH V9 2/5] i2c: tegra: add bus clear master support Sowjanya Komatineni
@ 2019-02-01 19:06   ` Dmitry Osipenko
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 19:06 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, Sowjanya Komatineni пишет:
> Bus clear feature of Tegra I2C controller helps to recover from
> bus hang when I2C master loses the bus arbitration due to the
> slave device holding SDA LOW continuously for some unknown reasons.
> 
> Per I2C specification, the device that held the bus LOW should
> release it within 9 clock pulses.
> 
> During bus clear operation, Tegra I2C controller sends 9 clock
> pulses and terminates the transaction with STOP condition.
> Upon successful bus clear operation, bus goes to idle state and
> driver retries the transaction.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
>  [V5/V6/V7/V8]: Same as V4
>  [V4]: Added I2C Bus Clear support patch to this version of series.
> 
>  drivers/i2c/busses/i2c-tegra.c | 73 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 73 insertions(+)
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* Re: [PATCH V9 4/5] i2c: tegra: update transfer timeout
  2019-02-01 17:07 ` [PATCH V9 4/5] i2c: tegra: update transfer timeout Sowjanya Komatineni
@ 2019-02-01 19:16   ` Dmitry Osipenko
  2019-02-01 19:37     ` Sowjanya Komatineni
  0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 19:16 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, Sowjanya Komatineni пишет:
> Tegra194 allows max of 64K bytes and Tegra186 and prior allows
> max of 4K bytes of transfer per packet.
> 
> one sec timeout is not enough for transfers more than 10K bytes
> at STD bus rate.
> 
> This patch updates I2C transfer timeout based on the transfer size
> and I2C bus rate to allow enough time during max transfer size at
> lower bus speed.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
> 	Minor updates for readability of xfer time
>  [V8] : Added comment with explaination of xfer time calculation
>  [V5/V6/V7] : Same as V4
>  [V4] : V4 series includes bus clear support and this patch is updated with
> 	fixed timeout of 1sec for bus clear operation.
>  [V3] : Same as V2
>  [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 | 21 +++++++++++++++------
>  1 file changed, 15 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index ac8009c35863..43449495bfaa 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -25,7 +25,6 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/reset.h>
>  
> -#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
>  #define BYTES_PER_FIFO_WORD 4
>  
>  #define I2C_CNFG				0x000
> @@ -893,8 +892,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
>  		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
>  		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
>  
> -		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
> -							TEGRA_I2C_TIMEOUT);
> +		time_left = wait_for_completion_timeout(
> +						&i2c_dev->msg_complete,
> +						msecs_to_jiffies(1000));

So potentially tegra_i2c_xfer_msg() could take more than 1 second and then fail with -EAGAIN, correct? In that case we should set adapter.timeout in probe to a larger value:

  i2c_dev->adapter.timeout = 3 * HZ;



Otherwise looks good to me:

Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 18:57   ` Dmitry Osipenko
@ 2019-02-01 19:20     ` Sowjanya Komatineni
  2019-02-01 19:22       ` Dmitry Osipenko
  2019-02-01 20:21     ` Sowjanya Komatineni
  1 sibling, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 19:20 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(struct tegra_i2c_dev *i2c_dev) {
> > +	struct dma_chan *dma_chan;
> > +	u32 *dma_buf;
> > +	dma_addr_t dma_phys;
> > +	int err = 0;
> > +
> > +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> > +		return -ENODEV;
>
> Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.
>
> Should be:
>
>	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>		return 0;
>
Except EPROBE_DEFER, anything else returned from tegra_i2c_init_dma (ENODEV/ENOMEM) is ignored in i2c_probe
DMA mode decision is based on xfer size and availability of dma channel or can be changed based on valid dma buf to shorten the line.


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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 19:20     ` Sowjanya Komatineni
@ 2019-02-01 19:22       ` Dmitry Osipenko
  2019-02-02 14:13         ` Dmitry Osipenko
  0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 19:22 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 22:20, Sowjanya Komatineni пишет:
> 
>>> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
>>> +	struct dma_chan *dma_chan;
>>> +	u32 *dma_buf;
>>> +	dma_addr_t dma_phys;
>>> +	int err = 0;
>>> +
>>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>> +		return -ENODEV;
>>
>> Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.
>>
>> Should be:
>>
>> 	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>> 		return 0;
>>
> Except EPROBE_DEFER, anything else returned from tegra_i2c_init_dma (ENODEV/ENOMEM) is ignored in i2c_probe
> DMA mode decision is based on xfer size and availability of dma channel or can be changed based on valid dma buf to shorten the line.
> 

Ah, sorry. I missed that, seems good then.

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

* RE: [PATCH V9 4/5] i2c: tegra: update transfer timeout
  2019-02-01 19:16   ` Dmitry Osipenko
@ 2019-02-01 19:37     ` Sowjanya Komatineni
  2019-02-01 19:41       ` Dmitry Osipenko
  2019-02-04  7:54       ` Thierry Reding
  0 siblings, 2 replies; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 19:37 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c


> > BYTES_PER_FIFO_WORD 4
> >  
> >  #define I2C_CNFG				0x000
> > @@ -893,8 +892,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
> >  		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
> >  		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
> >  
> > -		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
> > -							TEGRA_I2C_TIMEOUT);
> > +		time_left = wait_for_completion_timeout(
> > +						&i2c_dev->msg_complete,
> > +						msecs_to_jiffies(1000));
>
> So potentially tegra_i2c_xfer_msg() could take more than 1 second and then fail with -EAGAIN, correct? In that case we should set adapter.timeout in probe to a larger value:
>
Bus clear pulse threshold we are setting is 9 (default as per spec) so bus clear should happen after finishing sending of 9 pulses.
So 1sec is very long time for bus to get released and in case of ARB LOST whole transfer to return EAGAIN will be much less then 1sec

>   i2c_dev->adapter.timeout = 3 * HZ;
>
>
> Otherwise looks good to me:
>
> Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* Re: [PATCH V9 4/5] i2c: tegra: update transfer timeout
  2019-02-01 19:37     ` Sowjanya Komatineni
@ 2019-02-01 19:41       ` Dmitry Osipenko
  2019-02-04  7:54       ` Thierry Reding
  1 sibling, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 19:41 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 22:37, Sowjanya Komatineni пишет:
> 
>>> BYTES_PER_FIFO_WORD 4
>>>  
>>>  #define I2C_CNFG				0x000
>>> @@ -893,8 +892,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
>>>  		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
>>>  		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
>>>  
>>> -		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
>>> -							TEGRA_I2C_TIMEOUT);
>>> +		time_left = wait_for_completion_timeout(
>>> +						&i2c_dev->msg_complete,
>>> +						msecs_to_jiffies(1000));
>>
>> So potentially tegra_i2c_xfer_msg() could take more than 1 second and then fail with -EAGAIN, correct? In that case we should set adapter.timeout in probe to a larger value:
>>
> Bus clear pulse threshold we are setting is 9 (default as per spec) so bus clear should happen after finishing sending of 9 pulses.
> So 1sec is very long time for bus to get released and in case of ARB LOST whole transfer to return EAGAIN will be much less then 1sec

Okay, thank you very much for the clarification. What about to set the timeout to a shorter value then? Maybe 10ms.

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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 18:57   ` Dmitry Osipenko
  2019-02-01 19:20     ` Sowjanya Komatineni
@ 2019-02-01 20:21     ` Sowjanya Komatineni
  2019-02-01 20:30       ` Dmitry Osipenko
  1 sibling, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 20:21 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c


>	rx_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
>	if (IS_ERR(rx_chan))
>		return PTR_ERR(rx_chan);
>
>
> > +
> > +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
> > +	if (IS_ERR(dma_chan)) {
> > +		err = PTR_ERR(dma_chan);
> > +		goto error;
>
> It's a good practice to release resources in opposite order to the allocation. Hence better to write this as:
>
>		goto err_release_rx;
>
> > +	}
> > +
> > +	i2c_dev->tx_dma_chan = dma_chan;
> > +
> > +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
> > +				     i2c_dev->dma_buf_size, &dma_phys,
> > +				     GFP_KERNEL | __GFP_NOWARN);
> > +
> > +	if (!dma_buf) {
> > +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
> > +		err = -ENOMEM;
> > +		goto error;
>
>		goto err_release_tx;
>
> > +	}
> > +
> > +	i2c_dev->dma_buf = dma_buf;
> > +	i2c_dev->dma_phys = dma_phys;	i2c_dev->rx_dma_chan = rx_chan;
>	i2c_dev->tx_dma_chan = tx_chan;
>
> > +	return 0;
> > +
> > +error:
> > +	if (i2c_dev->tx_dma_chan)
> > +		dma_release_channel(i2c_dev->tx_dma_chan);
> > +
> > +	if (i2c_dev->rx_dma_chan)
> > +		dma_release_channel(i2c_dev->rx_dma_chan);
>
> error_release_tx:
>	dma_release_channel(tx_chan);
> error_release_rx:
>	dma_release_channel(rx_chan);
>
> > +
> > +	return err;

I am releasing resources in reverse order to allocation.
Trying for rx allocation followed by tx allocation
During release releasing tx and then rx.
In case if tx allocation fails, doesn’t go thru release. If rx or buf allocation fails, releases tx first and then rx


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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 20:21     ` Sowjanya Komatineni
@ 2019-02-01 20:30       ` Dmitry Osipenko
  2019-02-01 21:36         ` Sowjanya Komatineni
  0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 20:30 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 23:21, Sowjanya Komatineni пишет:
> 
>> 	rx_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
>> 	if (IS_ERR(rx_chan))
>> 		return PTR_ERR(rx_chan);
>>
>>
>>> +
>>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
>>> +	if (IS_ERR(dma_chan)) {
>>> +		err = PTR_ERR(dma_chan);
>>> +		goto error;
>>
>> It's a good practice to release resources in opposite order to the allocation. Hence better to write this as:
>>
>> 		goto err_release_rx;
>>
>>> +	}
>>> +
>>> +	i2c_dev->tx_dma_chan = dma_chan;
>>> +
>>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
>>> +				     i2c_dev->dma_buf_size, &dma_phys,
>>> +				     GFP_KERNEL | __GFP_NOWARN);
>>> +
>>> +	if (!dma_buf) {
>>> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
>>> +		err = -ENOMEM;
>>> +		goto error;
>>
>> 		goto err_release_tx;
>>
>>> +	}
>>> +
>>> +	i2c_dev->dma_buf = dma_buf;
>>> +	i2c_dev->dma_phys = dma_phys;	

	i2c_dev->rx_dma_chan = rx_chan;
>> 	i2c_dev->tx_dma_chan = tx_chan;
>>
>>> +	return 0;
>>> +
>>> +error:
>>> +	if (i2c_dev->tx_dma_chan)
>>> +		dma_release_channel(i2c_dev->tx_dma_chan);
>>> +
>>> +	if (i2c_dev->rx_dma_chan)
>>> +		dma_release_channel(i2c_dev->rx_dma_chan);
>>
>> error_release_tx:
>> 	dma_release_channel(tx_chan);
>> error_release_rx:
>> 	dma_release_channel(rx_chan);
>>
>>> +
>>> +	return err;
> 
> I am releasing resources in reverse order to allocation.
> Trying for rx allocation followed by tx allocation
> During release releasing tx and then rx.
> In case if tx allocation fails, doesn’t go thru release. If rx or buf allocation fails, releases tx first and then rx
> 


Okay. Anyway it's a good-n-common practice to write it in the way I'm suggesting.

And please set rx_chan and tx_chan after dma_buf allocation as I'm suggesting because you current variant will crash kernel since if dma_buf allocation fails, both rx and tx channels will be released and you're not setting them to NULL in that case.

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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 20:30       ` Dmitry Osipenko
@ 2019-02-01 21:36         ` Sowjanya Komatineni
  2019-02-01 21:45           ` Dmitry Osipenko
  0 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-01 21:36 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c


> > 
> >> 	rx_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
> >> 	if (IS_ERR(rx_chan))
> >> 		return PTR_ERR(rx_chan); 
> >>
> >>
> >>> +
> >>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
> >>> +	if (IS_ERR(dma_chan)) {
> >>> +		err = PTR_ERR(dma_chan);
> >>> +		goto error;
> >>
> >> It's a good practice to release resources in opposite order to the allocation. Hence better to write this as:
> >>
> >> 		goto err_release_rx;
> >>
> >>> +	}
> >>> +
> >>> +	i2c_dev->tx_dma_chan = dma_chan;
> >>> +
> >>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
> >>> +				     i2c_dev->dma_buf_size, &dma_phys,
> >>> +				     GFP_KERNEL | __GFP_NOWARN);
> >>> +
> >>> +	if (!dma_buf) {
> >>> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
> >>> +		err = -ENOMEM;
> >>> +		goto error;
> >>
> >> 		goto err_release_tx;
> >>
> >>> +	}
> >>> +
> >>> +	i2c_dev->dma_buf = dma_buf;
> >>> +	i2c_dev->dma_phys = dma_phys;	
>
>	i2c_dev->rx_dma_chan = rx_chan;
> >> 	i2c_dev->tx_dma_chan = tx_chan;
> >>
> >>> +	return 0;
> >>> +
> >>> +error:
> >>> +	if (i2c_dev->tx_dma_chan)
> >>> +		dma_release_channel(i2c_dev->tx_dma_chan);
> >>> +
> >>> +	if (i2c_dev->rx_dma_chan)
> >>> +		dma_release_channel(i2c_dev->rx_dma_chan); 
> >>
> >> error_release_tx:
> >> 	dma_release_channel(tx_chan);
> >> error_release_rx:
> >> 	dma_release_channel(rx_chan);
> >>
> >>> +
> >>> +	return err;
> > 
> > I am releasing resources in reverse order to allocation.
> > Trying for rx allocation followed by tx allocation During release 
> > releasing tx and then rx.
> > In case if tx allocation fails, doesn’t go thru release. If rx or buf 
> > allocation fails, releases tx first and then rx
> > 
>
>
> Okay. Anyway it's a good-n-common practice to write it in the way I'm suggesting.
>
> And please set rx_chan and tx_chan after dma_buf allocation as I'm suggesting because you current variant will crash kernel since if dma_buf allocation fails, both rx and tx channels will be released and you're not setting them to NULL in that case.

OK, my wrong assumption. Thought dma_release_channel will NULL chan after its freed
Will update and send V10

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 21:36         ` Sowjanya Komatineni
@ 2019-02-01 21:45           ` Dmitry Osipenko
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-01 21:45 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

02.02.2019 0:36, Sowjanya Komatineni пишет:
> 
>>>
>>>> 	rx_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
>>>> 	if (IS_ERR(rx_chan))
>>>> 		return PTR_ERR(rx_chan); 
>>>>
>>>>
>>>>> +
>>>>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
>>>>> +	if (IS_ERR(dma_chan)) {
>>>>> +		err = PTR_ERR(dma_chan);
>>>>> +		goto error;
>>>>
>>>> It's a good practice to release resources in opposite order to the allocation. Hence better to write this as:
>>>>
>>>> 		goto err_release_rx;
>>>>
>>>>> +	}
>>>>> +
>>>>> +	i2c_dev->tx_dma_chan = dma_chan;
>>>>> +
>>>>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
>>>>> +				     i2c_dev->dma_buf_size, &dma_phys,
>>>>> +				     GFP_KERNEL | __GFP_NOWARN);
>>>>> +
>>>>> +	if (!dma_buf) {
>>>>> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
>>>>> +		err = -ENOMEM;
>>>>> +		goto error;
>>>>
>>>> 		goto err_release_tx;
>>>>
>>>>> +	}
>>>>> +
>>>>> +	i2c_dev->dma_buf = dma_buf;
>>>>> +	i2c_dev->dma_phys = dma_phys;	
>>
>> 	i2c_dev->rx_dma_chan = rx_chan;
>>>> 	i2c_dev->tx_dma_chan = tx_chan;
>>>>
>>>>> +	return 0;
>>>>> +
>>>>> +error:
>>>>> +	if (i2c_dev->tx_dma_chan)
>>>>> +		dma_release_channel(i2c_dev->tx_dma_chan);
>>>>> +
>>>>> +	if (i2c_dev->rx_dma_chan)
>>>>> +		dma_release_channel(i2c_dev->rx_dma_chan); 
>>>>
>>>> error_release_tx:
>>>> 	dma_release_channel(tx_chan);
>>>> error_release_rx:
>>>> 	dma_release_channel(rx_chan);
>>>>
>>>>> +
>>>>> +	return err;
>>>
>>> I am releasing resources in reverse order to allocation.
>>> Trying for rx allocation followed by tx allocation During release 
>>> releasing tx and then rx.
>>> In case if tx allocation fails, doesn’t go thru release. If rx or buf 
>>> allocation fails, releases tx first and then rx
>>>
>>
>>
>> Okay. Anyway it's a good-n-common practice to write it in the way I'm suggesting.
>>
>> And please set rx_chan and tx_chan after dma_buf allocation as I'm suggesting because you current variant will crash kernel since if dma_buf allocation fails, both rx and tx channels will be released and you're not setting them to NULL in that case.
> 
> OK, my wrong assumption. Thought dma_release_channel will NULL chan after its freed
> Will update and send V10
> 

There is no need to rush, you may wait at least for the testing feedback for now. Also maybe Thierry or somebody else will have something to say as well.

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
  2019-02-01 18:57   ` Dmitry Osipenko
@ 2019-02-02 14:01   ` Dmitry Osipenko
  2019-02-02 18:32     ` Sowjanya Komatineni
  2019-02-03 14:19   ` Dmitry Osipenko
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-02 14:01 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, 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. This adds delay between data bytes of the same
> transfer when CPU is fully loaded and some slave devices has
> internal timeout for no bus activity and stops transaction to
> avoid bus hang. DMA mode is helpful in such cases.
> 
> DMA mode is also helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
> 	Removed dependency of APB DMA in Kconfig and added conditional check
> 	in I2C driver to decide on using DMA mode.
> 	Changed back the allocation of dma buffer during i2c probe.
> 	Fixed FIFO triggers depending on DMA Vs PIO.
>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
> 	other fixes
> 	Updated Kconfig for APB_DMA dependency
>  [V7] : Same as V6
>  [V6] : Updated for proper buffer allocation/freeing, channel release.
> 	Updated to use exact xfer size for syncing dma buffer.
>  [V5] : Same as V4
>  [V4] : Updated to allocate DMA buffer only when DMA mode.
> 	Updated to fall back to PIO mode when DMA channel request or
> 	buffer allocation fails.
>  [V3] : Updated without additional buffer allocation.
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 335 insertions(+), 33 deletions(-)


[snip]


> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int err = 0;
> +
> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> +		return -ENODEV;

Another detail is that we need to keep older kernels working on T186+ after its device-tree will get the "dmas" property, device-tree changes shall be backwards-compatible with older kernels. Hence we need to check that platform actually wants the APB DMA driver, otherwise T186+ will be failing with -EPROBE_DEFER.

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 32d5744bce45..822a84013a6d 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -227,6 +227,7 @@ struct tegra_i2c_hw_feature {
 	u32 setup_hold_time_std_mode;
 	u32 setup_hold_time_fast_fast_plus_mode;
 	u32 setup_hold_time_hs_mode;
+	bool has_apb_dma;
 };
 
 /**
@@ -396,7 +397,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
 	dma_addr_t dma_phys;
 	int err = 0;
 
-	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
+	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) || !i2c_dev->hw->has_apb_dma)
 		return -ENODEV;
 
 	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
@@ -1283,6 +1284,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.setup_hold_time_std_mode = 0x0,
 	.setup_hold_time_fast_fast_plus_mode = 0x0,
 	.setup_hold_time_hs_mode = 0x0,
+	.has_apb_dma = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -1306,6 +1308,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.setup_hold_time_std_mode = 0x0,
 	.setup_hold_time_fast_fast_plus_mode = 0x0,
 	.setup_hold_time_hs_mode = 0x0,
+	.has_apb_dma = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -1329,6 +1332,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.setup_hold_time_std_mode = 0x0,
 	.setup_hold_time_fast_fast_plus_mode = 0x0,
 	.setup_hold_time_hs_mode = 0x0,
+	.has_apb_dma = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -1352,6 +1356,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.setup_hold_time_std_mode = 0x0,
 	.setup_hold_time_fast_fast_plus_mode = 0x0,
 	.setup_hold_time_hs_mode = 0x0,
+	.has_apb_dma = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -1375,6 +1380,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.setup_hold_time_std_mode = 0,
 	.setup_hold_time_fast_fast_plus_mode = 0,
 	.setup_hold_time_hs_mode = 0,
+	.has_apb_dma = true,
 };
 
 static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
@@ -1398,6 +1404,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
 	.setup_hold_time_std_mode = 0,
 	.setup_hold_time_fast_fast_plus_mode = 0,
 	.setup_hold_time_hs_mode = 0,
+	.has_apb_dma = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
@@ -1421,6 +1428,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
 	.setup_hold_time_std_mode = 0x08080808,
 	.setup_hold_time_fast_fast_plus_mode = 0x02020202,
 	.setup_hold_time_hs_mode = 0x090909,
+	.has_apb_dma = false,
 };
 
 /* Match table for of_platform binding */

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 19:22       ` Dmitry Osipenko
@ 2019-02-02 14:13         ` Dmitry Osipenko
  2019-02-02 18:43           ` Sowjanya Komatineni
  0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-02 14:13 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 22:22, Dmitry Osipenko пишет:
> 01.02.2019 22:20, Sowjanya Komatineni пишет:
>>
>>>> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
>>>> +	struct dma_chan *dma_chan;
>>>> +	u32 *dma_buf;
>>>> +	dma_addr_t dma_phys;
>>>> +	int err = 0;
>>>> +
>>>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>>> +		return -ENODEV;
>>>
>>> Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.
>>>
>>> Should be:
>>>
>>> 	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>> 		return 0;
>>>
>> Except EPROBE_DEFER, anything else returned from tegra_i2c_init_dma (ENODEV/ENOMEM) is ignored in i2c_probe
>> DMA mode decision is based on xfer size and availability of dma channel or can be changed based on valid dma buf to shorten the line.
>>
> 
> Ah, sorry. I missed that, seems good then.
> 

BTW, it may be worthwhile to move out the error code handling into tegra_i2c_init_dma() for clarity. It also won't hurt to not ignore errors other than -ENODEV.

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 32d5744bce45..684a0689ac8d 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -397,7 +397,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
 	int err = 0;
 
 	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
-		return -ENODEV;
+		return 0;
 
 	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
 	if (IS_ERR(dma_chan))
@@ -434,6 +434,13 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
 	if (i2c_dev->rx_dma_chan)
 		dma_release_channel(i2c_dev->rx_dma_chan);
 
+	/*
+	 * -ENODEV is likely due to a missing "dmas" property, driver falls
+	 * back to PIO in this case.
+	 */
+	if (err == -ENODEV)
+		return 0;
+
 	return err;
 }
 
@@ -1553,7 +1560,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 	}
 
 	ret = tegra_i2c_init_dma(i2c_dev);
-	if (ret == -EPROBE_DEFER)
+	if (ret)
 		goto disable_div_clk;
 
 	ret = tegra_i2c_init(i2c_dev, false);


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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-02 14:01   ` Dmitry Osipenko
@ 2019-02-02 18:32     ` Sowjanya Komatineni
  2019-02-03 16:48       ` Dmitry Osipenko
  0 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-02 18:32 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 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. This adds delay between data bytes of the same transfer 
> > when CPU is fully loaded and some slave devices has internal timeout 
> > for no bus activity and stops transaction to avoid bus hang. DMA mode 
> > is helpful in such cases.
> > 
> > DMA mode is also helpful for Large transfers during downloading or 
> > uploading FW over I2C to some external devices.
> > 
> > Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> > ---
>
>
> > +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
> > +	struct dma_chan *dma_chan;
> > +	u32 *dma_buf;
> > +	dma_addr_t dma_phys;
> > +	int err = 0;
> > +
> > +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> > +		return -ENODEV;
>
> Another detail is that we need to keep older kernels working on T186+ after its device-tree will get the "dmas" property, device-tree changes shall be backwards-compatible with older kernels. Hence we need to check that platform actually wants the APB DMA driver, otherwise T186+ will be failing with -EPROBE_DEFER.

Yes, that will be a separate patch later for adding DMA support for Tegra186 and later chips once we check on GPCDMA upstream



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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-02 14:13         ` Dmitry Osipenko
@ 2019-02-02 18:43           ` Sowjanya Komatineni
  2019-02-03 16:42             ` Dmitry Osipenko
  0 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-02 18:43 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(struct tegra_i2c_dev *i2c_dev) {
> >>>> +	struct dma_chan *dma_chan;
> >>>> +	u32 *dma_buf;
> >>>> +	dma_addr_t dma_phys;
> >>>> +	int err = 0;
> >>>> +
> >>>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> >>>> +		return -ENODEV;
> >>>
> >>> Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.
> >>>
> >>> Should be:
> >>>
> >>> 	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> >>> 		return 0;
> >>>
> >> Except EPROBE_DEFER, anything else returned from tegra_i2c_init_dma 
> >> (ENODEV/ENOMEM) is ignored in i2c_probe DMA mode decision is based on xfer size and availability of dma channel or can be changed based on valid dma buf to shorten the line.
> >>
> > 
> > Ah, sorry. I missed that, seems good then.
> > 
>
> BTW, it may be worthwhile to move out the error code handling into tegra_i2c_init_dma() for clarity. It also won't hurt to not ignore errors other than -ENODEV.

Either ways are same. To be more readable/clear on error types for diff cases and errors that we are ignoring, I am explicitly keeping error codecs (EPROBE_DEFER/ENODEV/ENOMEM) same way.
Also in probe for readability checking with EPROBE_DEFER which explicitly indicating we keep deferring i2c probe rather than simple return.


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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
  2019-02-01 18:57   ` Dmitry Osipenko
  2019-02-02 14:01   ` Dmitry Osipenko
@ 2019-02-03 14:19   ` Dmitry Osipenko
  2019-02-03 14:47   ` Dmitry Osipenko
  2019-02-04  8:04   ` Thierry Reding
  4 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-03 14:19 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, 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. This adds delay between data bytes of the same
> transfer when CPU is fully loaded and some slave devices has
> internal timeout for no bus activity and stops transaction to
> avoid bus hang. DMA mode is helpful in such cases.
> 
> DMA mode is also helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---

Good news! I tested patches on T20 and T30, everything works perfect this time. Please address the review comments and prepare the new version.

BTW, I noticed that there are more deferred probes in the KMSG log with this patch. That is unsurprising because Tegra I2C driver is registered from the subsys level and APB DMA driver from the module level, hence I2C device can probe successfully only after the APB DMA.

The small change helps to reduce the amount of deferring during boot, please consider including it into this patch:

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 32d5744bce45..e2049d5b0c00 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -1651,19 +1651,7 @@ static struct platform_driver tegra_i2c_driver = {
                .pm    = TEGRA_I2C_PM,
        },
 };
-
-static int __init tegra_i2c_init_driver(void)
-{
-       return platform_driver_register(&tegra_i2c_driver);
-}
-
-static void __exit tegra_i2c_exit_driver(void)
-{
-       platform_driver_unregister(&tegra_i2c_driver);
-}
-
-subsys_initcall(tegra_i2c_init_driver);
-module_exit(tegra_i2c_exit_driver);
+module_platform_driver(tegra_i2c_driver);
 
 MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver");
 MODULE_AUTHOR("Colin Cross");

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
                     ` (2 preceding siblings ...)
  2019-02-03 14:19   ` Dmitry Osipenko
@ 2019-02-03 14:47   ` Dmitry Osipenko
  2019-02-03 15:27     ` Dmitry Osipenko
  2019-02-04 12:57     ` Dmitry Osipenko
  2019-02-04  8:04   ` Thierry Reding
  4 siblings, 2 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-03 14:47 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

01.02.2019 20:07, 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. This adds delay between data bytes of the same
> transfer when CPU is fully loaded and some slave devices has
> internal timeout for no bus activity and stops transaction to
> avoid bus hang. DMA mode is helpful in such cases.
> 
> DMA mode is also helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
> 	Removed dependency of APB DMA in Kconfig and added conditional check
> 	in I2C driver to decide on using DMA mode.
> 	Changed back the allocation of dma buffer during i2c probe.
> 	Fixed FIFO triggers depending on DMA Vs PIO.
>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
> 	other fixes
> 	Updated Kconfig for APB_DMA dependency
>  [V7] : Same as V6
>  [V6] : Updated for proper buffer allocation/freeing, channel release.
> 	Updated to use exact xfer size for syncing dma buffer.
>  [V5] : Same as V4
>  [V4] : Updated to allocate DMA buffer only when DMA mode.
> 	Updated to fall back to PIO mode when DMA channel request or
> 	buffer allocation fails.
>  [V3] : Updated without additional buffer allocation.
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 335 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index 118b7023a0f4..ac8009c35863 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -8,6 +8,9 @@
>  
>  #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>
> @@ -44,6 +47,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
> @@ -125,6 +130,19 @@
>  #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
> +
> +#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.
> @@ -191,6 +209,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
> + * @base_phys: Physical base address of the I2C controller
>   * @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
> @@ -204,6 +223,13 @@ 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
> + * @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;
> @@ -213,6 +239,7 @@ struct tegra_i2c_dev {
>  	struct clk *fast_clk;
>  	struct reset_control *rst;
>  	void __iomem *base;
> +	phys_addr_t base_phys;
>  	int cont_id;
>  	int irq;
>  	bool irq_disabled;
> @@ -226,6 +253,13 @@ struct tegra_i2c_dev {
>  	u16 clk_divisor_non_hs_mode;
>  	bool is_multimaster_mode;
>  	spinlock_t xfer_lock;
> +	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 void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
> @@ -294,6 +328,86 @@ 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;
> +	struct dma_chan *chan;
> +
> +	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;
> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
> +	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(struct tegra_i2c_dev *i2c_dev)
> +{
> +	struct dma_chan *dma_chan;
> +	u32 *dma_buf;
> +	dma_addr_t dma_phys;
> +	int err = 0;
> +
> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> +		return -ENODEV;
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
> +	if (IS_ERR(dma_chan))
> +		return PTR_ERR(dma_chan);
> +
> +	i2c_dev->rx_dma_chan = dma_chan;
> +
> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
> +	if (IS_ERR(dma_chan)) {
> +		err = PTR_ERR(dma_chan);
> +		goto error;
> +	}
> +
> +	i2c_dev->tx_dma_chan = dma_chan;
> +
> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
> +				     i2c_dev->dma_buf_size, &dma_phys,
> +				     GFP_KERNEL | __GFP_NOWARN);
> +
> +	if (!dma_buf) {
> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
> +		err = -ENOMEM;
> +		goto error;
> +	}
> +
> +	i2c_dev->dma_buf = dma_buf;
> +	i2c_dev->dma_phys = dma_phys;
> +	return 0;
> +
> +error:
> +	if (i2c_dev->tx_dma_chan)
> +		dma_release_channel(i2c_dev->tx_dma_chan);
> +
> +	if (i2c_dev->rx_dma_chan)
> +		dma_release_channel(i2c_dev->rx_dma_chan);
> +
> +	return err;
> +}
> +
>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>  {
>  	unsigned long timeout = jiffies + HZ;
> @@ -571,16 +685,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
>  	}
>  
> -	if (i2c_dev->hw->has_mst_fifo) {
> -		val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
> -		      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
> -		i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
> -	} else {
> -		val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
> -			0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
> -		i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
> -	}
> -
>  	err = tegra_i2c_flush_fifos(i2c_dev);
>  	if (err)
>  		goto err;
> @@ -660,25 +764,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
>  		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);
>  
> +	/*
> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
> +	 * DMA completion and during message write XFER_COMPLETE interrupt is
> +	 * triggered after DMA completion.
> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
> +	 * so forcing msg_buf_remaining to 0 in DMA mode.
> +	 */
>  	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);
>  	}
> @@ -694,12 +810,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>  	if (i2c_dev->is_dvc)
>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>  
> +	if (i2c_dev->is_curr_dma_xfer) {
> +		if (i2c_dev->msg_read)
> +			dmaengine_terminate_all(i2c_dev->rx_dma_chan);
> +		else
> +			dmaengine_terminate_all(i2c_dev->tx_dma_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;

This structure is allocated on stack, please initialize it to 0:

	struct dma_slave_config dma_sconfig = { 0 };

> +	struct dma_chan *chan;
> +
> +	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->base_phys + 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->base_phys + I2C_RX_FIFO;
> +		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_sconfig.src_maxburst = dma_burst;
> +	}
> +
> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
> +	dmaengine_slave_config(chan, &dma_sconfig);
> +}

[snip]

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-03 14:47   ` Dmitry Osipenko
@ 2019-02-03 15:27     ` Dmitry Osipenko
  2019-02-04 12:57     ` Dmitry Osipenko
  1 sibling, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-03 15:27 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

03.02.2019 17:47, Dmitry Osipenko пишет:
> 01.02.2019 20:07, 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. This adds delay between data bytes of the same
>> transfer when CPU is fully loaded and some slave devices has
>> internal timeout for no bus activity and stops transaction to
>> avoid bus hang. DMA mode is helpful in such cases.
>>
>> DMA mode is also helpful for Large transfers during downloading or
>> uploading FW over I2C to some external devices.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>  [V9] : Rebased to 5.0-rc4
>> 	Removed dependency of APB DMA in Kconfig and added conditional check
>> 	in I2C driver to decide on using DMA mode.
>> 	Changed back the allocation of dma buffer during i2c probe.
>> 	Fixed FIFO triggers depending on DMA Vs PIO.
>>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
>> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
>> 	other fixes
>> 	Updated Kconfig for APB_DMA dependency
>>  [V7] : Same as V6
>>  [V6] : Updated for proper buffer allocation/freeing, channel release.
>> 	Updated to use exact xfer size for syncing dma buffer.
>>  [V5] : Same as V4
>>  [V4] : Updated to allocate DMA buffer only when DMA mode.
>> 	Updated to fall back to PIO mode when DMA channel request or
>> 	buffer allocation fails.
>>  [V3] : Updated without additional buffer allocation.
>>  [V2] : Updated based on V1 review feedback along with code cleanup for
>> 	proper implementation of DMA.
>>
>>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>>  1 file changed, 335 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
>> index 118b7023a0f4..ac8009c35863 100644
>> --- a/drivers/i2c/busses/i2c-tegra.c
>> +++ b/drivers/i2c/busses/i2c-tegra.c
>> @@ -8,6 +8,9 @@
>>  
>>  #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>
>> @@ -44,6 +47,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
>> @@ -125,6 +130,19 @@
>>  #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
>> +
>> +#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.
>> @@ -191,6 +209,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
>> + * @base_phys: Physical base address of the I2C controller
>>   * @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
>> @@ -204,6 +223,13 @@ 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
>> + * @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;
>> @@ -213,6 +239,7 @@ struct tegra_i2c_dev {
>>  	struct clk *fast_clk;
>>  	struct reset_control *rst;
>>  	void __iomem *base;
>> +	phys_addr_t base_phys;
>>  	int cont_id;
>>  	int irq;
>>  	bool irq_disabled;
>> @@ -226,6 +253,13 @@ struct tegra_i2c_dev {
>>  	u16 clk_divisor_non_hs_mode;
>>  	bool is_multimaster_mode;
>>  	spinlock_t xfer_lock;
>> +	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 void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>> @@ -294,6 +328,86 @@ 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;
>> +	struct dma_chan *chan;
>> +
>> +	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;
>> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
>> +	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(struct tegra_i2c_dev *i2c_dev)
>> +{
>> +	struct dma_chan *dma_chan;
>> +	u32 *dma_buf;
>> +	dma_addr_t dma_phys;
>> +	int err = 0;
>> +
>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>> +		return -ENODEV;
>> +
>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
>> +	if (IS_ERR(dma_chan))
>> +		return PTR_ERR(dma_chan);
>> +
>> +	i2c_dev->rx_dma_chan = dma_chan;
>> +
>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
>> +	if (IS_ERR(dma_chan)) {
>> +		err = PTR_ERR(dma_chan);
>> +		goto error;
>> +	}
>> +
>> +	i2c_dev->tx_dma_chan = dma_chan;
>> +
>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
>> +				     i2c_dev->dma_buf_size, &dma_phys,
>> +				     GFP_KERNEL | __GFP_NOWARN);
>> +
>> +	if (!dma_buf) {
>> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
>> +		err = -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	i2c_dev->dma_buf = dma_buf;
>> +	i2c_dev->dma_phys = dma_phys;
>> +	return 0;
>> +
>> +error:
>> +	if (i2c_dev->tx_dma_chan)
>> +		dma_release_channel(i2c_dev->tx_dma_chan);
>> +
>> +	if (i2c_dev->rx_dma_chan)
>> +		dma_release_channel(i2c_dev->rx_dma_chan);
>> +
>> +	return err;
>> +}
>> +
>>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>>  {
>>  	unsigned long timeout = jiffies + HZ;
>> @@ -571,16 +685,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>>  		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
>>  	}
>>  
>> -	if (i2c_dev->hw->has_mst_fifo) {
>> -		val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
>> -		      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
>> -		i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
>> -	} else {
>> -		val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
>> -			0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
>> -		i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
>> -	}
>> -
>>  	err = tegra_i2c_flush_fifos(i2c_dev);
>>  	if (err)
>>  		goto err;
>> @@ -660,25 +764,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>  	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
>>  		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);
>>  
>> +	/*
>> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
>> +	 * DMA completion and during message write XFER_COMPLETE interrupt is
>> +	 * triggered after DMA completion.
>> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
>> +	 * so forcing msg_buf_remaining to 0 in DMA mode.
>> +	 */
>>  	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);
>>  	}
>> @@ -694,12 +810,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>  	if (i2c_dev->is_dvc)
>>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>>  
>> +	if (i2c_dev->is_curr_dma_xfer) {
>> +		if (i2c_dev->msg_read)
>> +			dmaengine_terminate_all(i2c_dev->rx_dma_chan);
>> +		else
>> +			dmaengine_terminate_all(i2c_dev->tx_dma_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;
> 
> This structure is allocated on stack, please initialize it to 0:
> 
> 	struct dma_slave_config dma_sconfig = { 0 };
> 
>> +	struct dma_chan *chan;
>> +
>> +	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->base_phys + 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->base_phys + I2C_RX_FIFO;
>> +		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +		dma_sconfig.src_maxburst = dma_burst;
>> +	}
>> +
>> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;

BTW, you could write this as:

	if (direction == DATA_DMA_DIR_TX)
		chan = i2c_dev->tx_dma_chan;
	else
		chan = i2c_dev->rx_dma_chan;

for consistency

>> +	dmaengine_slave_config(chan, &dma_sconfig);
>> +}



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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-02 18:43           ` Sowjanya Komatineni
@ 2019-02-03 16:42             ` Dmitry Osipenko
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-03 16:42 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

02.02.2019 21:43, Sowjanya Komatineni пишет:
> 
>>>>>> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
>>>>>> +	struct dma_chan *dma_chan;
>>>>>> +	u32 *dma_buf;
>>>>>> +	dma_addr_t dma_phys;
>>>>>> +	int err = 0;
>>>>>> +
>>>>>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>>>>> +		return -ENODEV;
>>>>>
>>>>> Driver shall not fail to probe if DMA driver is disabled, but to continue with the PIO-only mode available.
>>>>>
>>>>> Should be:
>>>>>
>>>>> 	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>>>> 		return 0;
>>>>>
>>>> Except EPROBE_DEFER, anything else returned from tegra_i2c_init_dma 
>>>> (ENODEV/ENOMEM) is ignored in i2c_probe DMA mode decision is based on xfer size and availability of dma channel or can be changed based on valid dma buf to shorten the line.
>>>>
>>>
>>> Ah, sorry. I missed that, seems good then.
>>>
>>
>> BTW, it may be worthwhile to move out the error code handling into tegra_i2c_init_dma() for clarity. It also won't hurt to not ignore errors other than -ENODEV.
> 
> Either ways are same. To be more readable/clear on error types for diff cases and errors that we are ignoring, I am explicitly keeping error codecs (EPROBE_DEFER/ENODEV/ENOMEM) same way.
> Also in probe for readability checking with EPROBE_DEFER which explicitly indicating we keep deferring i2c probe rather than simple return.
> 

Okay, then please add a error message for the dma_chan request-failure, printing a message that tells error code for the case (or even regardless) of errno != -ENODEV.

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-02 18:32     ` Sowjanya Komatineni
@ 2019-02-03 16:48       ` Dmitry Osipenko
  2019-02-04  8:18         ` Thierry Reding
  0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-03 16:48 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

02.02.2019 21:32, 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. This adds delay between data bytes of the same transfer 
>>> when CPU is fully loaded and some slave devices has internal timeout 
>>> for no bus activity and stops transaction to avoid bus hang. DMA mode 
>>> is helpful in such cases.
>>>
>>> DMA mode is also helpful for Large transfers during downloading or 
>>> uploading FW over I2C to some external devices.
>>>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>
>>
>>> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
>>> +	struct dma_chan *dma_chan;
>>> +	u32 *dma_buf;
>>> +	dma_addr_t dma_phys;
>>> +	int err = 0;
>>> +
>>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>>> +		return -ENODEV;
>>
>> Another detail is that we need to keep older kernels working on T186+ after its device-tree will get the "dmas" property, device-tree changes shall be backwards-compatible with older kernels. Hence we need to check that platform actually wants the APB DMA driver, otherwise T186+ will be failing with -EPROBE_DEFER.
> 
> Yes, that will be a separate patch later for adding DMA support for Tegra186 and later chips once we check on GPCDMA upstream
> 
> 

Sure, and there is a requirement in upstream to keep older kernel versions working with the later device-tree updates, as I pointed above. Please include the ".has_apb_dma checking" that I'm suggesting into the next version.
 

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

* Re: [PATCH V9 4/5] i2c: tegra: update transfer timeout
  2019-02-01 19:37     ` Sowjanya Komatineni
  2019-02-01 19:41       ` Dmitry Osipenko
@ 2019-02-04  7:54       ` Thierry Reding
  1 sibling, 0 replies; 32+ messages in thread
From: Thierry Reding @ 2019-02-04  7:54 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: 1458 bytes --]

On Fri, Feb 01, 2019 at 07:37:45PM +0000, Sowjanya Komatineni wrote:
> 
> > > BYTES_PER_FIFO_WORD 4
> > >  
> > >  #define I2C_CNFG				0x000
> > > @@ -893,8 +892,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev)
> > >  		i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
> > >  		tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
> > >  
> > > -		time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
> > > -							TEGRA_I2C_TIMEOUT);
> > > +		time_left = wait_for_completion_timeout(
> > > +						&i2c_dev->msg_complete,
> > > +						msecs_to_jiffies(1000));
> >
> > So potentially tegra_i2c_xfer_msg() could take more than 1 second
> > and then fail with -EAGAIN, correct? In that case we should set
> > adapter.timeout in probe to a larger value:
> >
> Bus clear pulse threshold we are setting is 9 (default as per spec) so
> bus clear should happen after finishing sending of 9 pulses.
> So 1sec is very long time for bus to get released and in case of ARB
> LOST whole transfer to return EAGAIN will be much less then 1sec

What if for example we have a very long transfer, say some 64 KiB on
Tegra194 and about 95% through the transfer something causes the bus
to lock up. The transfer so far would've taken something on the order
of 5 seconds, but we'd still fail, potentially with -EAGAIN, right?

Or is there some other mechanism that would prevent the above from
happening?

Thierry

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

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
                     ` (3 preceding siblings ...)
  2019-02-03 14:47   ` Dmitry Osipenko
@ 2019-02-04  8:04   ` Thierry Reding
  4 siblings, 0 replies; 32+ messages in thread
From: Thierry Reding @ 2019-02-04  8:04 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, mkarthik, smohammed, talho, linux-tegra, linux-kernel,
	linux-i2c

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

On Fri, Feb 01, 2019 at 09:07:45AM -0800, Sowjanya Komatineni wrote:
> 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. This adds delay between data bytes of the same
> transfer when CPU is fully loaded and some slave devices has
> internal timeout for no bus activity and stops transaction to
> avoid bus hang. DMA mode is helpful in such cases.
> 
> DMA mode is also helpful for Large transfers during downloading or
> uploading FW over I2C to some external devices.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  [V9] : Rebased to 5.0-rc4
> 	Removed dependency of APB DMA in Kconfig and added conditional check
> 	in I2C driver to decide on using DMA mode.
> 	Changed back the allocation of dma buffer during i2c probe.
> 	Fixed FIFO triggers depending on DMA Vs PIO.
>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
> 	other fixes
> 	Updated Kconfig for APB_DMA dependency
>  [V7] : Same as V6
>  [V6] : Updated for proper buffer allocation/freeing, channel release.
> 	Updated to use exact xfer size for syncing dma buffer.
>  [V5] : Same as V4
>  [V4] : Updated to allocate DMA buffer only when DMA mode.
> 	Updated to fall back to PIO mode when DMA channel request or
> 	buffer allocation fails.
>  [V3] : Updated without additional buffer allocation.
>  [V2] : Updated based on V1 review feedback along with code cleanup for
> 	proper implementation of DMA.
> 
>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 335 insertions(+), 33 deletions(-)

With Dmitry's concern addressed:

Acked-by: Thierry Reding <treding@nvidia.com>

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

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-03 16:48       ` Dmitry Osipenko
@ 2019-02-04  8:18         ` Thierry Reding
  0 siblings, 0 replies; 32+ messages in thread
From: Thierry Reding @ 2019-02-04  8:18 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Sowjanya Komatineni, Jonathan Hunter, Mantravadi Karthik,
	Shardar Mohammed, Timo Alho, linux-tegra, linux-kernel,
	linux-i2c

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

On Sun, Feb 03, 2019 at 07:48:09PM +0300, Dmitry Osipenko wrote:
> 02.02.2019 21:32, 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. This adds delay between data bytes of the same transfer 
> >>> when CPU is fully loaded and some slave devices has internal timeout 
> >>> for no bus activity and stops transaction to avoid bus hang. DMA mode 
> >>> is helpful in such cases.
> >>>
> >>> DMA mode is also helpful for Large transfers during downloading or 
> >>> uploading FW over I2C to some external devices.
> >>>
> >>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> >>> ---
> >>
> >>
> >>> +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) {
> >>> +	struct dma_chan *dma_chan;
> >>> +	u32 *dma_buf;
> >>> +	dma_addr_t dma_phys;
> >>> +	int err = 0;
> >>> +
> >>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
> >>> +		return -ENODEV;
> >>
> >> Another detail is that we need to keep older kernels working on
> >> T186+ after its device-tree will get the "dmas" property,
> >> device-tree changes shall be backwards-compatible with older
> >> kernels. Hence we need to check that platform actually wants the
> >> APB DMA driver, otherwise T186+ will be failing with -EPROBE_DEFER.
> > 
> > Yes, that will be a separate patch later for adding DMA support for
> > Tegra186 and later chips once we check on GPCDMA upstream
> > 
> > 
> 
> Sure, and there is a requirement in upstream to keep older kernel
> versions working with the later device-tree updates, as I pointed
> above. Please include the ".has_apb_dma checking" that I'm suggesting
> into the next version.

I don't think that's true. The requirement is for the device-tree ABI to
remain stable, but that technically only requires that kernel updates do
not require a DTB update.

The assumption on which this is based is that the DTB may be shipping in
read-only storage, like it used to fairly commonly in the distant past.

Conversely, if you update the DTB the assumption is that you can also
update the kernel, so the compatibility doesn't need to go that way.

That said, I think what you're proposing here makes sense and is pretty
trivial, so might as well add it at this time.

Thierry

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

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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-03 14:47   ` Dmitry Osipenko
  2019-02-03 15:27     ` Dmitry Osipenko
@ 2019-02-04 12:57     ` Dmitry Osipenko
  2019-02-05  1:37       ` Sowjanya Komatineni
  1 sibling, 1 reply; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-04 12:57 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, mkarthik,
	smohammed, talho
  Cc: linux-tegra, linux-kernel, linux-i2c

03.02.2019 17:47, Dmitry Osipenko пишет:
> 01.02.2019 20:07, 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. This adds delay between data bytes of the same
>> transfer when CPU is fully loaded and some slave devices has
>> internal timeout for no bus activity and stops transaction to
>> avoid bus hang. DMA mode is helpful in such cases.
>>
>> DMA mode is also helpful for Large transfers during downloading or
>> uploading FW over I2C to some external devices.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>  [V9] : Rebased to 5.0-rc4
>> 	Removed dependency of APB DMA in Kconfig and added conditional check
>> 	in I2C driver to decide on using DMA mode.
>> 	Changed back the allocation of dma buffer during i2c probe.
>> 	Fixed FIFO triggers depending on DMA Vs PIO.
>>  [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE
>> 	interrupt and using PACKETS_XFER_COMPLETE interrupt only and some
>> 	other fixes
>> 	Updated Kconfig for APB_DMA dependency
>>  [V7] : Same as V6
>>  [V6] : Updated for proper buffer allocation/freeing, channel release.
>> 	Updated to use exact xfer size for syncing dma buffer.
>>  [V5] : Same as V4
>>  [V4] : Updated to allocate DMA buffer only when DMA mode.
>> 	Updated to fall back to PIO mode when DMA channel request or
>> 	buffer allocation fails.
>>  [V3] : Updated without additional buffer allocation.
>>  [V2] : Updated based on V1 review feedback along with code cleanup for
>> 	proper implementation of DMA.
>>
>>  drivers/i2c/busses/i2c-tegra.c | 368 +++++++++++++++++++++++++++++++++++++----
>>  1 file changed, 335 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
>> index 118b7023a0f4..ac8009c35863 100644
>> --- a/drivers/i2c/busses/i2c-tegra.c
>> +++ b/drivers/i2c/busses/i2c-tegra.c
>> @@ -8,6 +8,9 @@
>>  
>>  #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>
>> @@ -44,6 +47,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
>> @@ -125,6 +130,19 @@
>>  #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
>> +
>> +#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.
>> @@ -191,6 +209,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
>> + * @base_phys: Physical base address of the I2C controller
>>   * @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
>> @@ -204,6 +223,13 @@ 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
>> + * @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;
>> @@ -213,6 +239,7 @@ struct tegra_i2c_dev {
>>  	struct clk *fast_clk;
>>  	struct reset_control *rst;
>>  	void __iomem *base;
>> +	phys_addr_t base_phys;
>>  	int cont_id;
>>  	int irq;
>>  	bool irq_disabled;
>> @@ -226,6 +253,13 @@ struct tegra_i2c_dev {
>>  	u16 clk_divisor_non_hs_mode;
>>  	bool is_multimaster_mode;
>>  	spinlock_t xfer_lock;
>> +	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 void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
>> @@ -294,6 +328,86 @@ 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;
>> +	struct dma_chan *chan;
>> +
>> +	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;
>> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
>> +	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(struct tegra_i2c_dev *i2c_dev)
>> +{
>> +	struct dma_chan *dma_chan;
>> +	u32 *dma_buf;
>> +	dma_addr_t dma_phys;
>> +	int err = 0;
>> +
>> +	if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA))
>> +		return -ENODEV;
>> +
>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
>> +	if (IS_ERR(dma_chan))
>> +		return PTR_ERR(dma_chan);
>> +
>> +	i2c_dev->rx_dma_chan = dma_chan;
>> +
>> +	dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
>> +	if (IS_ERR(dma_chan)) {
>> +		err = PTR_ERR(dma_chan);
>> +		goto error;
>> +	}
>> +
>> +	i2c_dev->tx_dma_chan = dma_chan;
>> +
>> +	dma_buf = dma_alloc_coherent(i2c_dev->dev,
>> +				     i2c_dev->dma_buf_size, &dma_phys,
>> +				     GFP_KERNEL | __GFP_NOWARN);
>> +
>> +	if (!dma_buf) {
>> +		dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
>> +		err = -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	i2c_dev->dma_buf = dma_buf;
>> +	i2c_dev->dma_phys = dma_phys;
>> +	return 0;
>> +
>> +error:
>> +	if (i2c_dev->tx_dma_chan)
>> +		dma_release_channel(i2c_dev->tx_dma_chan);
>> +
>> +	if (i2c_dev->rx_dma_chan)
>> +		dma_release_channel(i2c_dev->rx_dma_chan);
>> +
>> +	return err;
>> +}
>> +
>>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>>  {
>>  	unsigned long timeout = jiffies + HZ;
>> @@ -571,16 +685,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>>  		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
>>  	}
>>  
>> -	if (i2c_dev->hw->has_mst_fifo) {
>> -		val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
>> -		      I2C_MST_FIFO_CONTROL_RX_TRIG(1);
>> -		i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
>> -	} else {
>> -		val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
>> -			0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
>> -		i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
>> -	}
>> -
>>  	err = tegra_i2c_flush_fifos(i2c_dev);
>>  	if (err)
>>  		goto err;
>> @@ -660,25 +764,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>  	if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
>>  		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);
>>  
>> +	/*
>> +	 * During message read XFER_COMPLETE interrupt is triggered prior to
>> +	 * DMA completion and during message write XFER_COMPLETE interrupt is
>> +	 * triggered after DMA completion.
>> +	 * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
>> +	 * so forcing msg_buf_remaining to 0 in DMA mode.
>> +	 */
>>  	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);
>>  	}
>> @@ -694,12 +810,69 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
>>  	if (i2c_dev->is_dvc)
>>  		dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
>>  
>> +	if (i2c_dev->is_curr_dma_xfer) {
>> +		if (i2c_dev->msg_read)
>> +			dmaengine_terminate_all(i2c_dev->rx_dma_chan);
>> +		else
>> +			dmaengine_terminate_all(i2c_dev->tx_dma_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;
> 
> This structure is allocated on stack, please initialize it to 0:
> 
> 	struct dma_slave_config dma_sconfig = { 0 };
> 
>> +	struct dma_chan *chan;
>> +
>> +	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->base_phys + I2C_TX_FIFO;
>> +		dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +		dma_sconfig.dst_maxburst = dma_burst;

I know that APB DMA driver enables flow control based on the channels spec, but still won't hurt to explicitly show that channels are flow-controlled. Ideally APB DMA driver should respect the device_fc field.

		dma_sconfig.device_fc = true;

>> +	} else {
>> +		dma_sconfig.src_addr = i2c_dev->base_phys + I2C_RX_FIFO;
>> +		dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +		dma_sconfig.src_maxburst = dma_burst;

		dma_sconfig.device_fc = true;

>> +	}
>> +
>> +	chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
>> +	dmaengine_slave_config(chan, &dma_sconfig);
>> +}


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

* RE: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-04 12:57     ` Dmitry Osipenko
@ 2019-02-05  1:37       ` Sowjanya Komatineni
  2019-02-05  6:20         ` Dmitry Osipenko
  0 siblings, 1 reply; 32+ messages in thread
From: Sowjanya Komatineni @ 2019-02-05  1:37 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

> I know that APB DMA driver enables flow control based on the channels spec, but still won't hurt to explicitly show that channels are flow-controlled. Ideally APB DMA driver should respect the device_fc field.
>
>		dma_sconfig.device_fc = true;


Dmitry,
Thanks for all feedback. Sent updated patch V10 which has all below V9 feedback changes.

- Added explicit flow control settings to dma slave config and error check so need to move releasing of dma resources to separate function as I am using it multiple places (when dma slave config failed, on tegra drive remove, tegra_i2c_init_dma).
As a part of this, moved error handling also inside init_dma as you suggested in earlier feedback.
- Added apbdma hw support flag to now allow Tegra186 and later use APBDMA driver.
- Updated to register tegra_i2c_driver from module level rather than subsys level.
- Fixed timeout for bus clear to 50ms (10ms is enough but considering slaves responding slow). Also added adapter timeout to 6s considering worst case transfer rate.
- other minor fixes.

Please review.
- Sowjanya


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

* Re: [PATCH V9 3/5] i2c: tegra: Add DMA support
  2019-02-05  1:37       ` Sowjanya Komatineni
@ 2019-02-05  6:20         ` Dmitry Osipenko
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Osipenko @ 2019-02-05  6:20 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, Jonathan Hunter,
	Mantravadi Karthik, Shardar Mohammed, Timo Alho
  Cc: linux-tegra, linux-kernel, linux-i2c

05.02.2019 4:37, Sowjanya Komatineni пишет:
>> I know that APB DMA driver enables flow control based on the channels spec, but still won't hurt to explicitly show that channels are flow-controlled. Ideally APB DMA driver should respect the device_fc field.
>>
>> 		dma_sconfig.device_fc = true;
> 
> 
> Dmitry,
> Thanks for all feedback. Sent updated patch V10 which has all below V9 feedback changes.
> 
> - Added explicit flow control settings to dma slave config and error check so need to move releasing of dma resources to separate function as I am using it multiple places (when dma slave config failed, on tegra drive remove, tegra_i2c_init_dma).
> As a part of this, moved error handling also inside init_dma as you suggested in earlier feedback.
> - Added apbdma hw support flag to now allow Tegra186 and later use APBDMA driver.
> - Updated to register tegra_i2c_driver from module level rather than subsys level.
> - Fixed timeout for bus clear to 50ms (10ms is enough but considering slaves responding slow). Also added adapter timeout to 6s considering worst case transfer rate.
> - other minor fixes.
> 
> Please review.
> - Sowjanya
> 

Thank you very much for keeping up with the new versions! The v10 is reviewed now. It is very close to final, but there is one new fatal bug there that leaves FIFO control register unconfigured in a case of error. I also pointed at some other minor cases that could be improved, please see my replies.

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

end of thread, other threads:[~2019-02-05  6:21 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-01 17:07 [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Sowjanya Komatineni
2019-02-01 17:07 ` [PATCH V9 2/5] i2c: tegra: add bus clear master support Sowjanya Komatineni
2019-02-01 19:06   ` Dmitry Osipenko
2019-02-01 17:07 ` [PATCH V9 3/5] i2c: tegra: Add DMA support Sowjanya Komatineni
2019-02-01 18:57   ` Dmitry Osipenko
2019-02-01 19:20     ` Sowjanya Komatineni
2019-02-01 19:22       ` Dmitry Osipenko
2019-02-02 14:13         ` Dmitry Osipenko
2019-02-02 18:43           ` Sowjanya Komatineni
2019-02-03 16:42             ` Dmitry Osipenko
2019-02-01 20:21     ` Sowjanya Komatineni
2019-02-01 20:30       ` Dmitry Osipenko
2019-02-01 21:36         ` Sowjanya Komatineni
2019-02-01 21:45           ` Dmitry Osipenko
2019-02-02 14:01   ` Dmitry Osipenko
2019-02-02 18:32     ` Sowjanya Komatineni
2019-02-03 16:48       ` Dmitry Osipenko
2019-02-04  8:18         ` Thierry Reding
2019-02-03 14:19   ` Dmitry Osipenko
2019-02-03 14:47   ` Dmitry Osipenko
2019-02-03 15:27     ` Dmitry Osipenko
2019-02-04 12:57     ` Dmitry Osipenko
2019-02-05  1:37       ` Sowjanya Komatineni
2019-02-05  6:20         ` Dmitry Osipenko
2019-02-04  8:04   ` Thierry Reding
2019-02-01 17:07 ` [PATCH V9 4/5] i2c: tegra: update transfer timeout Sowjanya Komatineni
2019-02-01 19:16   ` Dmitry Osipenko
2019-02-01 19:37     ` Sowjanya Komatineni
2019-02-01 19:41       ` Dmitry Osipenko
2019-02-04  7:54       ` Thierry Reding
2019-02-01 17:07 ` [PATCH V9 5/5] i2c: tegra: add i2c interface timing support Sowjanya Komatineni
2019-02-01 19:05 ` [PATCH V9 1/5] i2c: tegra: sort all the include headers alphabetically Dmitry Osipenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).