Re: [PATCH 1/2] i2c: tegra: enable multi master mode

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

 



On Mon, Mar 14, 2016 at 02:14:33PM +0530, Shardar Shariff Md wrote:
> Enable multi-master mode in I2C_CNFG reg based on hw features.
> Using single/multi-master mode bit introduced for Tegra210,
> whereas multi-master mode is enabled by default in HW for T124 and
> earlier Tegra SOC. Enabling this bit doesn't explicitly start
> treating the bus has having multiple masters, but will start
> checking for arbitration lost and reporting when it occurs.
> 
> The Tegra210 I2C controller supports single/multi master mode.
> Add chipdata for Tegra210 and its compatibility string so that
> Tegra210 will select data that enables multi master mode correctly.
> 
> Add "nvidia,multimaster-mode" property to enable multimaster specific
> prerequisites:

Please check Documentation/devicetree/bindings/i2c/i2c.txt. There is a
generic "multi-master" binding already.

> 1. Enable 1st level clock always set.
> 2. Disable 2nd level clock gating (slcg which is supported from
>    T124 SOC and later chips)
> 
> Signed-off-by: Shardar Shariff Md <smohammed@xxxxxxxxxx>
> ---
>  drivers/i2c/busses/i2c-tegra.c | 70 ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 64 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
> index efba1eb..ce99d02 100644
> --- a/drivers/i2c/busses/i2c-tegra.c
> +++ b/drivers/i2c/busses/i2c-tegra.c
> @@ -38,6 +38,7 @@
>  #define I2C_CNFG_DEBOUNCE_CNT_SHIFT		12
>  #define I2C_CNFG_PACKET_MODE_EN			(1<<10)
>  #define I2C_CNFG_NEW_MASTER_FSM			(1<<11)
> +#define I2C_CNFG_MULTI_MASTER_MODE		(1<<17)
>  #define I2C_STATUS				0x01C
>  #define I2C_SL_CNFG				0x020
>  #define I2C_SL_CNFG_NACK			(1<<1)
> @@ -133,6 +134,8 @@ struct tegra_i2c_hw_feature {
>  	bool has_single_clk_source;
>  	int clk_divisor_hs_mode;
>  	int clk_divisor_std_fast_mode;
> +	bool has_multi_master_mode;
> +	bool has_slcg_override_reg;
>  };
>  
>  /**
> @@ -173,6 +176,7 @@ struct tegra_i2c_dev {
>  	int msg_read;
>  	u32 bus_clk_rate;
>  	bool is_suspended;
> +	bool is_multimaster_mode;
>  };
>  
>  static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
> @@ -185,6 +189,9 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
>  	return readl(i2c_dev->base + reg);
>  }
>  
> +#define I2C_CLKEN_OVERRIDE			0x090
> +#define I2C_MST_CORE_CLKEN_OVR			(1 << 0)
> +
>  /*
>   * i2c_writel and i2c_readl will offset the register if necessary to talk
>   * to the I2C block inside the DVC block
> @@ -424,6 +431,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  
>  	val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
>  		(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
> +
> +	if (i2c_dev->hw->has_multi_master_mode)
> +		val |= I2C_CNFG_MULTI_MASTER_MODE;
> +
>  	i2c_writel(i2c_dev, val, I2C_CNFG);
>  	i2c_writel(i2c_dev, 0, I2C_INT_MASK);
>  
> @@ -449,6 +460,9 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
>  	if (tegra_i2c_flush_fifos(i2c_dev))
>  		err = -ETIMEDOUT;
>  
> +	if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
> +		i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
> +
>  	tegra_i2c_clock_disable(i2c_dev);
>  
>  	if (i2c_dev->irq_disabled) {
> @@ -660,6 +674,20 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
>  	return ret;
>  }
>  
> +static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
> +{
> +	struct device_node *np = i2c_dev->dev->of_node;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "clock-frequency",
> +			&i2c_dev->bus_clk_rate);
> +	if (ret)
> +		i2c_dev->bus_clk_rate = 100000; /* default clock rate */
> +
> +	i2c_dev->is_multimaster_mode = of_property_read_bool(np,
> +			"nvidia,multimaster-mode");
> +}
> +
>  static const struct i2c_algorithm tegra_i2c_algo = {
>  	.master_xfer	= tegra_i2c_xfer,
>  	.functionality	= tegra_i2c_func,
> @@ -671,6 +699,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
>  	.has_single_clk_source = false,
>  	.clk_divisor_hs_mode = 3,
>  	.clk_divisor_std_fast_mode = 0,
> +	.has_multi_master_mode = false,
> +	.has_slcg_override_reg = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
> @@ -679,6 +709,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
>  	.has_single_clk_source = false,
>  	.clk_divisor_hs_mode = 3,
>  	.clk_divisor_std_fast_mode = 0,
> +	.has_multi_master_mode = false,
> +	.has_slcg_override_reg = false,
>  };
>  
>  static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
> @@ -687,10 +719,25 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
>  	.has_single_clk_source = true,
>  	.clk_divisor_hs_mode = 1,
>  	.clk_divisor_std_fast_mode = 0x19,
> +	.has_multi_master_mode = false,
> +	.has_slcg_override_reg = false,
>  };
>  
> +static const struct tegra_i2c_hw_feature tegra210_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,
> +	.has_config_load_reg = true,
> +	.has_multi_master_mode = true,
> +	.has_slcg_override_reg = true,
> +};
> +
> +
>  /* Match table for of_platform binding */
>  static const struct of_device_id tegra_i2c_of_match[] = {
> +	{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
>  	{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
>  	{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
>  	{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
> @@ -745,10 +792,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		return PTR_ERR(i2c_dev->rst);
>  	}
>  
> -	ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency",
> -					&i2c_dev->bus_clk_rate);
> -	if (ret)
> -		i2c_dev->bus_clk_rate = 100000; /* default clock rate */
> +	tegra_i2c_parse_dt(i2c_dev);
>  
>  	i2c_dev->hw = &tegra20_i2c_hw;
>  
> @@ -796,6 +840,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  		goto unprepare_fast_clk;
>  	}
>  
> +	if (i2c_dev->is_multimaster_mode) {
> +		ret = clk_enable(i2c_dev->div_clk);
> +		dev_err(i2c_dev->dev, "div_clk enable failed %d\n",
> +				ret);
> +		goto unprepare_div_clk;
> +	}
> +
>  	ret = tegra_i2c_init(i2c_dev);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to initialize i2c controller");
> @@ -806,7 +857,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  			tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
> -		goto unprepare_div_clk;
> +		goto disable_div_clk;
>  	}
>  
>  	i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
> @@ -822,11 +873,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
>  	ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to add I2C adapter\n");
> -		goto unprepare_div_clk;
> +		goto disable_div_clk;
>  	}
>  
>  	return 0;
>  
> +disable_div_clk:
> +	if (i2c_dev->is_multimaster_mode)
> +		clk_disable(i2c_dev->div_clk);
> +
>  unprepare_div_clk:
>  	clk_unprepare(i2c_dev->div_clk);
>  
> @@ -842,6 +897,9 @@ static int tegra_i2c_remove(struct platform_device *pdev)
>  	struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
>  	i2c_del_adapter(&i2c_dev->adapter);
>  
> +	if (i2c_dev->is_multimaster_mode)
> +		clk_disable(i2c_dev->div_clk);
> +
>  	clk_unprepare(i2c_dev->div_clk);
>  	if (!i2c_dev->hw->has_single_clk_source)
>  		clk_unprepare(i2c_dev->fast_clk);
> -- 
> 1.8.1.5
> 

Attachment: signature.asc
Description: PGP signature


[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