i2c: tegra: Add support for Tegra194

Message ID 20180618150450.13723-1-thierry.reding@gmail.com
State New
Headers show
Series
  • i2c: tegra: Add support for Tegra194
Related show

Commit Message

Thierry Reding June 18, 2018, 3:04 p.m.
From: Thierry Reding <treding@nvidia.com>

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@nvidia.com>
---
 drivers/i2c/busses/i2c-tegra.c | 99 +++++++++++++++++++++++++++++-----
 1 file changed, 85 insertions(+), 14 deletions(-)

Comments

Dmitry Osipenko June 18, 2018, 4:59 p.m. | #1
On Monday, 18 June 2018 18:04:50 MSK Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> 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@nvidia.com>
> ---
>  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);
> +	*/

Is it a deliberately commented out code?


--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jon Hunter June 19, 2018, 10:28 a.m. | #2
On 18/06/18 17:59, Dmitry Osipenko wrote:
> On Monday, 18 June 2018 18:04:50 MSK Thierry Reding wrote:
>> From: Thierry Reding <treding@nvidia.com>
>>
>> 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@nvidia.com>
>> ---
>>  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);
>> +	*/
> 
> Is it a deliberately commented out code?

This does look odd and I am guessing it was not meant to be part of this
patch. If so and you drop the above snippet, as it looks like it should be
a separate patch if uncommented, then for the rest of the patch ...

Acked-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon
Thierry Reding June 19, 2018, 10:41 a.m. | #3
On Mon, Jun 18, 2018 at 07:59:51PM +0300, Dmitry Osipenko wrote:
> On Monday, 18 June 2018 18:04:50 MSK Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > 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@nvidia.com>
> > ---
> >  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);
> > +	*/
> 
> Is it a deliberately commented out code?

I think I left this in by mistake. The code certainly did work the last
time I tried with the above commented out, so I'll just remove it.

Thierry

Patch

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, },