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

From: Wolfram Sang
Date: Mon Mar 14 2016 - 05:02:23 EST


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