mbox series

[0/5] Add I2C support for Tegra264

Message ID 20250108110620.86900-1-kkartik@nvidia.com
Headers show
Series Add I2C support for Tegra264 | expand

Message

Kartik Rajput Jan. 8, 2025, 11:06 a.m. UTC
Following series of patches add support for Tegra264 and High Speed (HS)
Mode in i2c-tegra.c driver.

Akhil R (3):
  i2c: tegra: Add HS mode support
  i2c: tegra: Add Tegra264 support
  i2c: tegra: Add support for SW Mutex register

Kartik Rajput (2):
  dt-bindings: i2c: nvidia,tegra20-i2c: Document Tegra264 I2C
  i2c: tegra: Do not configure DMA if not supported

 .../bindings/i2c/nvidia,tegra20-i2c.yaml      |   6 +
 drivers/i2c/busses/i2c-tegra.c                | 179 ++++++++++++++++--
 2 files changed, 170 insertions(+), 15 deletions(-)

Comments

Thierry Reding Jan. 8, 2025, 5:01 p.m. UTC | #1
On Wed, Jan 08, 2025 at 04:36:19PM +0530, Kartik Rajput wrote:
> From: Akhil R <akhilrajeev@nvidia.com>
> 
> Add support for SW Mutex register introduced in Tegra264 to provide

The spelling is a bit inconsistent. Earlier you referred to this as SW
MUTEX register, which makes sense if that's what it's called. But then
you call it "SW Mutex" register here. If you don't want to refer to it
by the documented name, it should probably be "SW mutex" instead.

> an option to share the interface between multiple firmware and/or

"firmwares"

> Virtual Machines.

"virtual machines" or "VMs"

> However, the hardware does not ensure any protection based on the
> values. The driver/firmware should honor the peer who already holds
> the mutex.
> 
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> ---
>  drivers/i2c/busses/i2c-tegra.c | 126 +++++++++++++++++++++++++++++----
>  1 file changed, 111 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index cf05937cb826..a5974af5b1af 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -135,6 +135,11 @@
>  #define I2C_MST_FIFO_STATUS_TX			GENMASK(23, 16)
>  #define I2C_MST_FIFO_STATUS_RX			GENMASK(7, 0)
>  
> +#define I2C_SW_MUTEX				0x0ec
> +#define I2C_SW_MUTEX_REQUEST			GENMASK(3, 0)
> +#define I2C_SW_MUTEX_GRANT			GENMASK(7, 4)
> +#define I2C_SW_MUTEX_ID				9
> +
>  /* configuration load timeout in microseconds */
>  #define I2C_CONFIG_LOAD_TIMEOUT			1000000
>  
> @@ -202,6 +207,7 @@ enum msg_end_type {
>   *		in HS mode.
>   * @has_interface_timing_reg: Has interface timing register to program the tuned
>   *		timing settings.
> + * @has_mutex: Has Mutex register for mutual exclusion with other firmwares or VM.

"mutex"

>   */
>  struct tegra_i2c_hw_feature {
>  	bool has_continue_xfer_support;
> @@ -228,6 +234,7 @@ struct tegra_i2c_hw_feature {
>  	u32 setup_hold_time_hs_mode;
>  	bool has_interface_timing_reg;
>  	bool has_hs_mode_support;
> +	bool has_mutex;
>  };
>  
>  /**
> @@ -371,6 +378,99 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
>  	readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
>  }
>  
> +static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
> +				   u32 reg, u32 mask, u32 delay_us,
> +				   u32 timeout_us)
> +{
> +	void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
> +	u32 val;
> +
> +	if (!i2c_dev->atomic_mode)
> +		return readl_relaxed_poll_timeout(addr, val, !(val & mask),
> +						  delay_us, timeout_us);
> +
> +	return readl_relaxed_poll_timeout_atomic(addr, val, !(val & mask),
> +						 delay_us, timeout_us);
> +}
> +
> +static int tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev)
> +{
> +	u32 val, id;
> +
> +	val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> +	id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> +	if (id != 0)
> +		return 0;
> +
> +	val = FIELD_PREP(I2C_SW_MUTEX_REQUEST, I2C_SW_MUTEX_ID);
> +	i2c_writel(i2c_dev, val, I2C_SW_MUTEX);
> +
> +	val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> +	id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> +
> +	if (id != I2C_SW_MUTEX_ID)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static void tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
> +{
> +	/* Poll until mutex is acquired. */
> +	while (tegra_i2c_mutex_trylock(i2c_dev))
> +		cpu_relax();
> +}

Don't we want to use a timeout here? Otherwise we risk blocking the
thread that this runs on if some firmware decides not to release the
mutex.

Also, is the logic not the wrong way around? I.e. trylock returns true
if the hardware mutex was successfully locked, in which case it doesn't
make sense to keep spinning, right? Or do I misunderstand how this
works?

Thierry

> +
> +static void tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
> +{
> +	u32 val, id;
> +
> +	val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> +	id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> +
> +	if (WARN_ON(id != I2C_SW_MUTEX_ID))
> +		return;
> +
> +	i2c_writel(i2c_dev, 0, I2C_SW_MUTEX);
> +}
> +
> +static void tegra_i2c_bus_lock(struct i2c_adapter *adapter,
> +			       unsigned int flags)
> +{
> +	struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> +
> +	rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
> +	tegra_i2c_mutex_lock(i2c_dev);
> +}
> +
> +static int tegra_i2c_bus_trylock(struct i2c_adapter *adapter,
> +				  unsigned int flags)
> +{
> +	struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> +	int ret;
> +
> +	ret = rt_mutex_trylock(&adapter->bus_lock);
> +	if (ret)
> +		ret = tegra_i2c_mutex_trylock(i2c_dev);
> +
> +	return ret;
> +}
> +
> +static void tegra_i2c_bus_unlock(struct i2c_adapter *adapter,
> +				 unsigned int flags)
> +{
> +	struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> +
> +	rt_mutex_unlock(&adapter->bus_lock);
> +	tegra_i2c_mutex_unlock(i2c_dev);
> +}
> +
> +static const struct i2c_lock_operations tegra_i2c_lock_ops = {
> +	.lock_bus = tegra_i2c_bus_lock,
> +	.trylock_bus = tegra_i2c_bus_trylock,
> +	.unlock_bus = tegra_i2c_bus_unlock,
> +};
> +
>  static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
>  {
>  	u32 int_mask;
> @@ -546,21 +646,6 @@ static void tegra_i2c_vi_init(struct tegra_i2c_dev *i2c_dev)
>  	i2c_writel(i2c_dev, 0x0, I2C_TLOW_SEXT);
>  }
>  
> -static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
> -				   u32 reg, u32 mask, u32 delay_us,
> -				   u32 timeout_us)
> -{
> -	void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
> -	u32 val;
> -
> -	if (!i2c_dev->atomic_mode)
> -		return readl_relaxed_poll_timeout(addr, val, !(val & mask),
> -						  delay_us, timeout_us);
> -
> -	return readl_relaxed_poll_timeout_atomic(addr, val, !(val & mask),
> -						 delay_us, timeout_us);
> -}
> -
>  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
>  {
>  	u32 mask, val, offset;
> @@ -1497,6 +1582,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0x0,
>  	.setup_hold_time_hs_mode = 0x0,
>  	.has_interface_timing_reg = false,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
> @@ -1521,6 +1607,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0x0,
>  	.setup_hold_time_hs_mode = 0x0,
>  	.has_interface_timing_reg = false,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
> @@ -1545,6 +1632,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0x0,
>  	.setup_hold_time_hs_mode = 0x0,
>  	.has_interface_timing_reg = false,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
> @@ -1569,6 +1657,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0x0,
>  	.setup_hold_time_hs_mode = 0x0,
>  	.has_interface_timing_reg = true,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
> @@ -1593,6 +1682,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0,
>  	.setup_hold_time_hs_mode = 0,
>  	.has_interface_timing_reg = true,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
> @@ -1617,6 +1707,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
>  	.setup_hold_time_fast_fast_plus_mode = 0,
>  	.setup_hold_time_hs_mode = 0,
>  	.has_interface_timing_reg = true,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
> @@ -1644,6 +1735,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
>  	.setup_hold_time_hs_mode = 0x090909,
>  	.has_interface_timing_reg = true,
>  	.has_hs_mode_support = true,
> +	.has_mutex = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
> @@ -1671,6 +1763,7 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
>  	.setup_hold_time_hs_mode = 0x090909,
>  	.has_interface_timing_reg = true,
>  	.has_hs_mode_support = true,
> +	.has_mutex = true,
>  };
>  
>  static const struct of_device_id tegra_i2c_of_match[] = {
> @@ -1875,6 +1968,9 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  	i2c_dev->adapter.nr = pdev->id;
>  	ACPI_COMPANION_SET(&i2c_dev->adapter.dev, ACPI_COMPANION(&pdev->dev));
>  
> +	if (i2c_dev->hw->has_mutex)
> +		i2c_dev->adapter.lock_ops = &tegra_i2c_lock_ops;
> +
>  	if (i2c_dev->hw->supports_bus_clear)
>  		i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info;
>  
> -- 
> 2.43.0
>
Thierry Reding Jan. 9, 2025, 10:24 a.m. UTC | #2
On Thu, Jan 09, 2025 at 10:13:48AM +0530, Mukesh Kumar Savaliya wrote:
> Hi Kartik,
> 
> On 1/8/2025 4:36 PM, Kartik Rajput wrote:
> > From: Akhil R <akhilrajeev@nvidia.com>
> > 
> > Add support for Tegra264 SoC which supports 17 generic I2C controllers,
> > two of which are in the AON (always-on) partition of the SoC. Tegra264
> > I2C supports all the features supported by Tegra194 I2C controllers.
> > 
> > Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> > Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> > ---
> >   drivers/i2c/busses/i2c-tegra.c | 28 ++++++++++++++++++++++++++++
> >   1 file changed, 28 insertions(+)
> > 
> > diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> > index 7b97c6d347ee..cf05937cb826 100644
> > --- a/drivers/i2c/busses/i2c-tegra.c
> > +++ b/drivers/i2c/busses/i2c-tegra.c
> > @@ -1646,7 +1646,35 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
> >   	.has_hs_mode_support = true,
> >   };
> > +static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
> I could see 7 controllers have been already added. And this may keep
> growing.

I'm not sure I understand the concern here. This is IP that's been in
use ever since the first Tegra chip was released about 15 years ago.
It's quite normal that the list of supported hardware will grow over
time. At the same time there will be occasional improvements of the
hardware that require certain parameterization.

> Can we make either default set which is common for most of and change only
> sepcific fields ?

It's difficult to do. These are const structures on purpose so that they
can go into .rodata, so as such there's no good way to reuse defaults. I
suppose we could do something like add preprocessor defines, but I doubt
that they would make things any better (these are quite fine-grained, so
macros would likely only cover one or two fields at a time).

> Second option - read these fields from DT and overwrite default if it's
> mentioned in DTSI.

Some information is already parsed from DT. What's in this structure can
all be derived from the compatible string, hence why it's associated
with the compatible string via the of_device_id table. Moreover, we
cannot move any of this information out into device tree (at least not
for existing chips) because it would break DT ABI.

> Please review and see if this makes sense. what others say ?

I'm always open to suggestions, but I also don't see this as very
problematic. It's data that is cleanly structured out, not difficult to
maintain and doesn't take up a huge amount of space.

Thierry
Thierry Reding Jan. 9, 2025, 10:37 a.m. UTC | #3
On Wed, Jan 08, 2025 at 04:36:15PM +0530, Kartik Rajput wrote:
> Following series of patches add support for Tegra264 and High Speed (HS)
> Mode in i2c-tegra.c driver.
> 
> Akhil R (3):
>   i2c: tegra: Add HS mode support
>   i2c: tegra: Add Tegra264 support
>   i2c: tegra: Add support for SW Mutex register
> 
> Kartik Rajput (2):
>   dt-bindings: i2c: nvidia,tegra20-i2c: Document Tegra264 I2C
>   i2c: tegra: Do not configure DMA if not supported

It'd probably make sense to restructure this series a little bit. It's
customary to have the DT patch first. It would then make more sense to
add preparatory patches and then follow those up by the Tegra264 support
patches, so something like this:

	dt-bindings: i2c: nvidia,tegra20-i2c: Document Tegra264 I2C
	i2c: tegra: Do not configure DMA if not supported
	i2c: tegra: Add HS mode support
	i2c: tegra: Add support for SW Mutex register
	i2c: tegra: Add Tegra264 support

Thierry
Mukesh Kumar Savaliya Jan. 9, 2025, 12:37 p.m. UTC | #4
Thanks Thierry !

On 1/9/2025 3:54 PM, Thierry Reding wrote:
> On Thu, Jan 09, 2025 at 10:13:48AM +0530, Mukesh Kumar Savaliya wrote:
>> Hi Kartik,
>>
>> On 1/8/2025 4:36 PM, Kartik Rajput wrote:
>>> From: Akhil R <akhilrajeev@nvidia.com>
>>>
>>> Add support for Tegra264 SoC which supports 17 generic I2C controllers,
>>> two of which are in the AON (always-on) partition of the SoC. Tegra264
>>> I2C supports all the features supported by Tegra194 I2C controllers.
>>>
>>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>>> Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
>>> ---
>>>    drivers/i2c/busses/i2c-tegra.c | 28 ++++++++++++++++++++++++++++
>>>    1 file changed, 28 insertions(+)
>>>
>>> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
>>> index 7b97c6d347ee..cf05937cb826 100644
>>> --- a/drivers/i2c/busses/i2c-tegra.c
>>> +++ b/drivers/i2c/busses/i2c-tegra.c
>>> @@ -1646,7 +1646,35 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
>>>    	.has_hs_mode_support = true,
>>>    };
>>> +static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
>> I could see 7 controllers have been already added. And this may keep
>> growing.
> 
> I'm not sure I understand the concern here. This is IP that's been in
> use ever since the first Tegra chip was released about 15 years ago.
> It's quite normal that the list of supported hardware will grow over
> time. At the same time there will be occasional improvements of the
> hardware that require certain parameterization.
> 
yes, i understand it can grow with new controllers. Was trying to 
optimize the growing list with common fields.

Example: tegra30_i2c_hw and tegra20_i2c_hw has one field changing
from 20 fields. So was thinking after seeing this commonality.

One suggestion: can one structure be default and then delta can be 
overridden ?

No concern if no other way as you mentioned below.

>> Can we make either default set which is common for most of and change only
>> sepcific fields ?
> 
> It's difficult to do. These are const structures on purpose so that they
> can go into .rodata, so as such there's no good way to reuse defaults. I
> suppose we could do something like add preprocessor defines, but I doubt
> that they would make things any better (these are quite fine-grained, so
> macros would likely only cover one or two fields at a time).
> 
Sure. Let's wait for others opinion. I understand complexity.

>> Second option - read these fields from DT and overwrite default if it's
>> mentioned in DTSI.
> 
> Some information is already parsed from DT. What's in this structure can
> all be derived from the compatible string, hence why it's associated
> with the compatible string via the of_device_id table. Moreover, we
> cannot move any of this information out into device tree (at least not
> for existing chips) because it would break DT ABI.
> 
Got it.
>> Please review and see if this makes sense. what others say ?
> 
> I'm always open to suggestions, but I also don't see this as very
> problematic. It's data that is cleanly structured out, not difficult to
> maintain and doesn't take up a huge amount of space.
> 
I Agree.
> Thierry
Rob Herring Jan. 10, 2025, 3:55 p.m. UTC | #5
On Wed, Jan 08, 2025 at 04:36:17PM +0530, Kartik Rajput wrote:
> Tegra264 has 17 generic I2C controllers, two of which are in always-on
> partition of the SoC. In addition to the features supported by Tegra194
> it also supports a MUTEX register to allow sharing the same I2C instance
> across multiple firmware.
> 
> Document compatible string "nvidia,tegra264-i2c" for Tegra264 I2C.
> 
> Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> ---
>  .../devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml         | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> index b57ae6963e62..2a016359328e 100644
> --- a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> +++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> @@ -80,6 +80,12 @@ properties:
>            support for 64 KiB transactions whereas earlier chips supported no
>            more than 4 KiB per transactions.
>          const: nvidia,tegra194-i2c
> +      - description: |

Don't need '|' if no formatting.

> +          Tegra264 has 17 generic I2C controllers, two of which are in the AON
> +          (always-on) partition of the SoC. In addition to the features from
> +          T194, a MUTEX register is added to support use of the same I2C
> +          instance across multiple firmware.
> +        const: nvidia,tegra264-i2c
>  
>    reg:
>      maxItems: 1
> -- 
> 2.43.0
>
Kartik Rajput Jan. 15, 2025, 11:39 a.m. UTC | #6
Thanks for reviewing the patch Thierry!


On Wed, 2025-01-08 at 18:01 +0100, Thierry Reding wrote:
> On Wed, Jan 08, 2025 at 04:36:19PM +0530, Kartik Rajput wrote:
> > From: Akhil R <akhilrajeev@nvidia.com>
> > 
> > Add support for SW Mutex register introduced in Tegra264 to provide
> 
> The spelling is a bit inconsistent. Earlier you referred to this as
> SW
> MUTEX register, which makes sense if that's what it's called. But
> then
> you call it "SW Mutex" register here. If you don't want to refer to
> it
> by the documented name, it should probably be "SW mutex" instead.
> 
> > an option to share the interface between multiple firmware and/or
> 
> "firmwares"
> 
> > Virtual Machines.
> 
> "virtual machines" or "VMs"
> 

ACK. I will fix this in the next patchset.

> > However, the hardware does not ensure any protection based on the
> > values. The driver/firmware should honor the peer who already holds
> > the mutex.
> > 
> > Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> > Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> > ---
> >  drivers/i2c/busses/i2c-tegra.c | 126
> > +++++++++++++++++++++++++++++----
> >  1 file changed, 111 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/i2c/busses/i2c-tegra.c
> > b/drivers/i2c/busses/i2c-tegra.c
> > index cf05937cb826..a5974af5b1af 100644
> > --- a/drivers/i2c/busses/i2c-tegra.c
> > +++ b/drivers/i2c/busses/i2c-tegra.c
> > @@ -135,6 +135,11 @@
> >  #define I2C_MST_FIFO_STATUS_TX                 GENMASK(23, 16)
> >  #define I2C_MST_FIFO_STATUS_RX                 GENMASK(7, 0)
> >  
> > +#define I2C_SW_MUTEX                           0x0ec
> > +#define I2C_SW_MUTEX_REQUEST                   GENMASK(3, 0)
> > +#define I2C_SW_MUTEX_GRANT                     GENMASK(7, 4)
> > +#define I2C_SW_MUTEX_ID                                9
> > +
> >  /* configuration load timeout in microseconds */
> >  #define I2C_CONFIG_LOAD_TIMEOUT                        1000000
> >  
> > @@ -202,6 +207,7 @@ enum msg_end_type {
> >   *             in HS mode.
> >   * @has_interface_timing_reg: Has interface timing register to
> > program the tuned
> >   *             timing settings.
> > + * @has_mutex: Has Mutex register for mutual exclusion with other
> > firmwares or VM.
> 
> "mutex"
> 
> >   */
> >  struct tegra_i2c_hw_feature {
> >         bool has_continue_xfer_support;
> > @@ -228,6 +234,7 @@ struct tegra_i2c_hw_feature {
> >         u32 setup_hold_time_hs_mode;
> >         bool has_interface_timing_reg;
> >         bool has_hs_mode_support;
> > +       bool has_mutex;
> >  };
> >  
> >  /**
> > @@ -371,6 +378,99 @@ static void i2c_readsl(struct tegra_i2c_dev
> > *i2c_dev, void *data,
> >         readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg),
> > data, len);
> >  }
> >  
> > +static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
> > +                                  u32 reg, u32 mask, u32 delay_us,
> > +                                  u32 timeout_us)
> > +{
> > +       void __iomem *addr = i2c_dev->base +
> > tegra_i2c_reg_addr(i2c_dev, reg);
> > +       u32 val;
> > +
> > +       if (!i2c_dev->atomic_mode)
> > +               return readl_relaxed_poll_timeout(addr, val, !(val
> > & mask),
> > +                                                 delay_us,
> > timeout_us);
> > +
> > +       return readl_relaxed_poll_timeout_atomic(addr, val, !(val &
> > mask),
> > +                                                delay_us,
> > timeout_us);
> > +}
> > +
> > +static int tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev)
> > +{
> > +       u32 val, id;
> > +
> > +       val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> > +       id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> > +       if (id != 0)
> > +               return 0;
> > +
> > +       val = FIELD_PREP(I2C_SW_MUTEX_REQUEST, I2C_SW_MUTEX_ID);
> > +       i2c_writel(i2c_dev, val, I2C_SW_MUTEX);
> > +
> > +       val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> > +       id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> > +
> > +       if (id != I2C_SW_MUTEX_ID)
> > +               return 0;
> > +
> > +       return 1;
> > +}
> > +
> > +static void tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
> > +{
> > +       /* Poll until mutex is acquired. */
> > +       while (tegra_i2c_mutex_trylock(i2c_dev))
> > +               cpu_relax();
> > +}
> 
> Don't we want to use a timeout here? Otherwise we risk blocking the
> thread that this runs on if some firmware decides not to release the
> mutex.
> 

We can continue to access the controller with a warning in case
the request times out.

Something like this?

static void tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
{
        unsigned int num_retries = 25; // Move this to a macro.

        /* Poll until mutex is acquired or timeout. */
        while ( --num_retries && !tegra_i2c_mutex_trylock(i2c_dev))
                msleep(1);

        WARN_ON(!num_retries);
}


> Also, is the logic not the wrong way around? I.e. trylock returns
> true
> if the hardware mutex was successfully locked, in which case it
> doesn't
> make sense to keep spinning, right? Or do I misunderstand how this
> works?
> 
> Thierry
> 

The logic is indeed wrong here, apologies for the oversight. I will fix
this in the next patchset.


> > +
> > +static void tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
> > +{
> > +       u32 val, id;
> > +
> > +       val = i2c_readl(i2c_dev, I2C_SW_MUTEX);
> > +       id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
> > +
> > +       if (WARN_ON(id != I2C_SW_MUTEX_ID))
> > +               return;
> > +
> > +       i2c_writel(i2c_dev, 0, I2C_SW_MUTEX);
> > +}
> > +
> > +static void tegra_i2c_bus_lock(struct i2c_adapter *adapter,
> > +                              unsigned int flags)
> > +{
> > +       struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> > +
> > +       rt_mutex_lock_nested(&adapter->bus_lock,
> > i2c_adapter_depth(adapter));
> > +       tegra_i2c_mutex_lock(i2c_dev);
> > +}
> > +
> > +static int tegra_i2c_bus_trylock(struct i2c_adapter *adapter,
> > +                                 unsigned int flags)
> > +{
> > +       struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> > +       int ret;
> > +
> > +       ret = rt_mutex_trylock(&adapter->bus_lock);
> > +       if (ret)
> > +               ret = tegra_i2c_mutex_trylock(i2c_dev);
> > +
> > +       return ret;
> > +}
> > +
> > +static void tegra_i2c_bus_unlock(struct i2c_adapter *adapter,
> > +                                unsigned int flags)
> > +{
> > +       struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adapter);
> > +
> > +       rt_mutex_unlock(&adapter->bus_lock);
> > +       tegra_i2c_mutex_unlock(i2c_dev);
> > +}
> > +
> > +static const struct i2c_lock_operations tegra_i2c_lock_ops = {
> > +       .lock_bus = tegra_i2c_bus_lock,
> > +       .trylock_bus = tegra_i2c_bus_trylock,
> > +       .unlock_bus = tegra_i2c_bus_unlock,
> > +};
> > +
> >  static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32
> > mask)
> >  {
> >         u32 int_mask;
> > @@ -546,21 +646,6 @@ static void tegra_i2c_vi_init(struct
> > tegra_i2c_dev *i2c_dev)
> >         i2c_writel(i2c_dev, 0x0, I2C_TLOW_SEXT);
> >  }
> >  
> > -static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
> > -                                  u32 reg, u32 mask, u32 delay_us,
> > -                                  u32 timeout_us)
> > -{
> > -       void __iomem *addr = i2c_dev->base +
> > tegra_i2c_reg_addr(i2c_dev, reg);
> > -       u32 val;
> > -
> > -       if (!i2c_dev->atomic_mode)
> > -               return readl_relaxed_poll_timeout(addr, val, !(val
> > & mask),
> > -                                                 delay_us,
> > timeout_us);
> > -
> > -       return readl_relaxed_poll_timeout_atomic(addr, val, !(val &
> > mask),
> > -                                                delay_us,
> > timeout_us);
> > -}
> > -
> >  static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
> >  {
> >         u32 mask, val, offset;
> > @@ -1497,6 +1582,7 @@ static const struct tegra_i2c_hw_feature
> > tegra20_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0x0,
> >         .setup_hold_time_hs_mode = 0x0,
> >         .has_interface_timing_reg = false,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
> > @@ -1521,6 +1607,7 @@ static const struct tegra_i2c_hw_feature
> > tegra30_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0x0,
> >         .setup_hold_time_hs_mode = 0x0,
> >         .has_interface_timing_reg = false,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
> > @@ -1545,6 +1632,7 @@ static const struct tegra_i2c_hw_feature
> > tegra114_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0x0,
> >         .setup_hold_time_hs_mode = 0x0,
> >         .has_interface_timing_reg = false,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
> > @@ -1569,6 +1657,7 @@ static const struct tegra_i2c_hw_feature
> > tegra124_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0x0,
> >         .setup_hold_time_hs_mode = 0x0,
> >         .has_interface_timing_reg = true,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
> > @@ -1593,6 +1682,7 @@ static const struct tegra_i2c_hw_feature
> > tegra210_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0,
> >         .setup_hold_time_hs_mode = 0,
> >         .has_interface_timing_reg = true,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
> > @@ -1617,6 +1707,7 @@ static const struct tegra_i2c_hw_feature
> > tegra186_i2c_hw = {
> >         .setup_hold_time_fast_fast_plus_mode = 0,
> >         .setup_hold_time_hs_mode = 0,
> >         .has_interface_timing_reg = true,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
> > @@ -1644,6 +1735,7 @@ static const struct tegra_i2c_hw_feature
> > tegra194_i2c_hw = {
> >         .setup_hold_time_hs_mode = 0x090909,
> >         .has_interface_timing_reg = true,
> >         .has_hs_mode_support = true,
> > +       .has_mutex = false,
> >  };
> >  
> >  static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
> > @@ -1671,6 +1763,7 @@ static const struct tegra_i2c_hw_feature
> > tegra264_i2c_hw = {
> >         .setup_hold_time_hs_mode = 0x090909,
> >         .has_interface_timing_reg = true,
> >         .has_hs_mode_support = true,
> > +       .has_mutex = true,
> >  };
> >  
> >  static const struct of_device_id tegra_i2c_of_match[] = {
> > @@ -1875,6 +1968,9 @@ static int tegra_i2c_probe(struct
> > platform_device *pdev)
> >         i2c_dev->adapter.nr = pdev->id;
> >         ACPI_COMPANION_SET(&i2c_dev->adapter.dev,
> > ACPI_COMPANION(&pdev->dev));
> >  
> > +       if (i2c_dev->hw->has_mutex)
> > +               i2c_dev->adapter.lock_ops = &tegra_i2c_lock_ops;
> > +
> >         if (i2c_dev->hw->supports_bus_clear)
> >                 i2c_dev->adapter.bus_recovery_info =
> > &tegra_i2c_recovery_info;
> >  
> > -- 
> > 2.43.0
> >
Kartik Rajput Jan. 15, 2025, 11:41 a.m. UTC | #7
On Wed, 2025-01-08 at 17:47 +0100, Thierry Reding wrote:
> On Wed, Jan 08, 2025 at 04:36:17PM +0530, Kartik Rajput wrote:
> > Tegra264 has 17 generic I2C controllers, two of which are in
> > always-on
> > partition of the SoC. In addition to the features supported by
> > Tegra194
> > it also supports a MUTEX register to allow sharing the same I2C
> > instance
> > across multiple firmware.
> > 
> > Document compatible string "nvidia,tegra264-i2c" for Tegra264 I2C.
> > 
> > Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> > ---
> >  .../devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml         | 6
> > ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-
> > i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-
> > i2c.yaml
> > index b57ae6963e62..2a016359328e 100644
> > --- a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> > +++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> > @@ -80,6 +80,12 @@ properties:
> >            support for 64 KiB transactions whereas earlier chips
> > supported no
> >            more than 4 KiB per transactions.
> >          const: nvidia,tegra194-i2c
> > +      - description: |
> > +          Tegra264 has 17 generic I2C controllers, two of which
> > are in the AON
> > +          (always-on) partition of the SoC. In addition to the
> > features from
> > +          T194, a MUTEX register is added to support use of the
> > same I2C
> 
> Maybe spell out Tegra194 above for consistency?
> 
> > +          instance across multiple firmware.
> 
> I don't know if this last sentence makes much sense in a DT bindings
> document, but doesn't hurt either. Maybe s/firmware/firmwares/.
> 
> Thierry

ACK. I will update this in the next patch set.
Kartik Rajput Jan. 15, 2025, 11:44 a.m. UTC | #8
On Thu, 2025-01-09 at 11:37 +0100, Thierry Reding wrote:
> On Wed, Jan 08, 2025 at 04:36:15PM +0530, Kartik Rajput wrote:
> > Following series of patches add support for Tegra264 and High Speed
> > (HS)
> > Mode in i2c-tegra.c driver.
> > 
> > Akhil R (3):
> >   i2c: tegra: Add HS mode support
> >   i2c: tegra: Add Tegra264 support
> >   i2c: tegra: Add support for SW Mutex register
> > 
> > Kartik Rajput (2):
> >   dt-bindings: i2c: nvidia,tegra20-i2c: Document Tegra264 I2C
> >   i2c: tegra: Do not configure DMA if not supported
> 
> It'd probably make sense to restructure this series a little bit.
> It's
> customary to have the DT patch first. It would then make more sense
> to
> add preparatory patches and then follow those up by the Tegra264
> support
> patches, so something like this:
> 
>         dt-bindings: i2c: nvidia,tegra20-i2c: Document Tegra264 I2C
>         i2c: tegra: Do not configure DMA if not supported
>         i2c: tegra: Add HS mode support
>         i2c: tegra: Add support for SW Mutex register
>         i2c: tegra: Add Tegra264 support
> 
> Thierry

Thanks for reviewing the patches in this series Thierry!
I will re-arrange this and post a new patch set for review.

Thanks & Regards,
Kartik
Kartik Rajput Jan. 15, 2025, 11:46 a.m. UTC | #9
Thanks Rob!

On Fri, 2025-01-10 at 09:55 -0600, Rob Herring wrote:
> External email: Use caution opening links or attachments
> 
> 
> On Wed, Jan 08, 2025 at 04:36:17PM +0530, Kartik Rajput wrote:
> > Tegra264 has 17 generic I2C controllers, two of which are in
> > always-on
> > partition of the SoC. In addition to the features supported by
> > Tegra194
> > it also supports a MUTEX register to allow sharing the same I2C
> > instance
> > across multiple firmware.
> > 
> > Document compatible string "nvidia,tegra264-i2c" for Tegra264 I2C.
> > 
> > Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
> > ---
> >  .../devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml         | 6
> > ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-
> > i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-
> > i2c.yaml
> > index b57ae6963e62..2a016359328e 100644
> > --- a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> > +++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml
> > @@ -80,6 +80,12 @@ properties:
> >            support for 64 KiB transactions whereas earlier chips
> > supported no
> >            more than 4 KiB per transactions.
> >          const: nvidia,tegra194-i2c
> > +      - description: |
> 
> Don't need '|' if no formatting.
> 

ACK. Will fix this in v2.

Thanks & Regards,
Kartik