[PATCH] i2c: tegra: Add support for Tegra194

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Thierry Reding <treding@xxxxxxxxxx>

In order to support advanced features, the I2C FIFO interface was
changed in the version of the Tegra I2C controller found in Tegra194.
The changes are backwards incompatible, so the driver needs to be
programmed in a slightly different way on new chips.

Add support for MST FIFO programming and add an OF match entry for
Tegra194. At the same time, mark all prior generations of this
controller as not having the MST FIFO interface.

Signed-off-by: Thierry Reding <treding@xxxxxxxxxx>
---
 drivers/i2c/busses/i2c-tegra.c | 99 +++++++++++++++++++++++++++++-----
 1 file changed, 85 insertions(+), 14 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 5fccd1f1bca8..a2779e9af1ff 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -115,6 +115,18 @@
 
 #define I2C_CONFIG_LOAD_TIMEOUT			1000000
 
+#define I2C_MST_FIFO_CONTROL			0x0b4
+#define I2C_MST_FIFO_CONTROL_RX_FLUSH		BIT(0)
+#define I2C_MST_FIFO_CONTROL_TX_FLUSH		BIT(1)
+#define I2C_MST_FIFO_CONTROL_RX_TRIG(x)		(((x) - 1) <<  4)
+#define I2C_MST_FIFO_CONTROL_TX_TRIG(x)		(((x) - 1) << 16)
+
+#define I2C_MST_FIFO_STATUS			0x0b8
+#define I2C_MST_FIFO_STATUS_RX_MASK		0xff
+#define I2C_MST_FIFO_STATUS_RX_SHIFT		0
+#define I2C_MST_FIFO_STATUS_TX_MASK		0xff0000
+#define I2C_MST_FIFO_STATUS_TX_SHIFT		16
+
 /*
  * 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.
@@ -154,6 +166,7 @@ struct tegra_i2c_hw_feature {
 	u16 clk_divisor_fast_plus_mode;
 	bool has_multi_master_mode;
 	bool has_slcg_override_reg;
+	bool has_mst_fifo;
 };
 
 /**
@@ -266,13 +279,24 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
 static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
 {
 	unsigned long timeout = jiffies + HZ;
-	u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL);
+	unsigned int offset;
+	u32 mask, val;
+
+	if (i2c_dev->hw->has_mst_fifo) {
+		mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
+		       I2C_MST_FIFO_CONTROL_RX_FLUSH;
+		offset = I2C_MST_FIFO_CONTROL;
+	} else {
+		mask = I2C_FIFO_CONTROL_TX_FLUSH |
+		       I2C_FIFO_CONTROL_RX_FLUSH;
+		offset = I2C_FIFO_CONTROL;
+	}
 
-	val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH;
-	i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+	val = i2c_readl(i2c_dev, offset);
+	val |= mask;
+	i2c_writel(i2c_dev, val, offset);
 
-	while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) &
-		(I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) {
+	while (i2c_readl(i2c_dev, offset) & mask) {
 		if (time_after(jiffies, timeout)) {
 			dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
 			return -ETIMEDOUT;
@@ -290,9 +314,15 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
 	size_t buf_remaining = i2c_dev->msg_buf_remaining;
 	int words_to_transfer;
 
-	val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
-	rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
-		I2C_FIFO_STATUS_RX_SHIFT;
+	if (i2c_dev->hw->has_mst_fifo) {
+		val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+		rx_fifo_avail = (val & I2C_MST_FIFO_STATUS_RX_MASK) >>
+			I2C_MST_FIFO_STATUS_RX_SHIFT;
+	} else {
+		val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+		rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
+			I2C_FIFO_STATUS_RX_SHIFT;
+	}
 
 	/* Rounds down to not include partial word at the end of buf */
 	words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
@@ -321,6 +351,16 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
 	BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
 	i2c_dev->msg_buf_remaining = buf_remaining;
 	i2c_dev->msg_buf = buf;
+
+	/*
+	 * All bytes received, mask RX_FIFO_DATA_REQ to prevent more
+	 * interrupts from FIFO.
+	 */
+	/*
+	if (i2c_dev->msg_buf_remaining == 0)
+		tegra_i2c_mask_irq(i2c_dev, I2C_INT_RX_FIFO_DATA_REQ);
+	*/
+
 	return 0;
 }
 
@@ -332,9 +372,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
 	size_t buf_remaining = i2c_dev->msg_buf_remaining;
 	int words_to_transfer;
 
-	val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
-	tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
-		I2C_FIFO_STATUS_TX_SHIFT;
+	if (i2c_dev->hw->has_mst_fifo) {
+		val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+		tx_fifo_avail = (val & I2C_MST_FIFO_STATUS_TX_MASK) >>
+			I2C_MST_FIFO_STATUS_TX_SHIFT;
+	} else {
+		val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+		tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
+			I2C_FIFO_STATUS_TX_SHIFT;
+	}
 
 	/* Rounds down to not include partial word at the end of buf */
 	words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
@@ -516,9 +562,15 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 		i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
 	}
 
-	val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
-		0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
-	i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+	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)
@@ -803,6 +855,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
 	.has_slcg_override_reg = false,
+	.has_mst_fifo = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -815,6 +868,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
 	.has_slcg_override_reg = false,
+	.has_mst_fifo = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -827,6 +881,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.has_config_load_reg = false,
 	.has_multi_master_mode = false,
 	.has_slcg_override_reg = false,
+	.has_mst_fifo = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -839,6 +894,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.has_config_load_reg = true,
 	.has_multi_master_mode = false,
 	.has_slcg_override_reg = true,
+	.has_mst_fifo = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -851,10 +907,25 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.has_config_load_reg = true,
 	.has_multi_master_mode = true,
 	.has_slcg_override_reg = true,
+	.has_mst_fifo = false,
+};
+
+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_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,
 };
 
 /* 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,tegra210-i2c", .data = &tegra210_i2c_hw, },
 	{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
 	{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
-- 
2.17.0




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux