These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / i2c / busses / i2c-tegra.c
index 1bcd75e..a0522fc 100644 (file)
 #define I2C_HEADER_CONTINUE_XFER               (1<<15)
 #define I2C_HEADER_MASTER_ADDR_SHIFT           12
 #define I2C_HEADER_SLAVE_ADDR_SHIFT            1
+
+#define I2C_CONFIG_LOAD                                0x08C
+#define I2C_MSTR_CONFIG_LOAD                   (1 << 0)
+#define I2C_SLV_CONFIG_LOAD                    (1 << 1)
+#define I2C_TIMEOUT_CONFIG_LOAD                        (1 << 2)
+
 /*
  * 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.
@@ -121,6 +127,8 @@ enum msg_end_type {
  * @has_single_clk_source: The i2c controller has single clock source. Tegra30
  *             and earlier Socs has two clock sources i.e. div-clk and
  *             fast-clk.
+ * @has_config_load_reg: Has the config load register to load the new
+ *             configuration.
  * @clk_divisor_hs_mode: Clock divisor in HS mode.
  * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
  *             applicable if there is no fast clock source i.e. single clock
@@ -131,8 +139,10 @@ struct tegra_i2c_hw_feature {
        bool has_continue_xfer_support;
        bool has_per_pkt_xfer_complete_irq;
        bool has_single_clk_source;
+       bool has_config_load_reg;
        int clk_divisor_hs_mode;
        int clk_divisor_std_fast_mode;
+       u16 clk_divisor_fast_plus_mode;
 };
 
 /**
@@ -172,6 +182,7 @@ struct tegra_i2c_dev {
        size_t msg_buf_remaining;
        int msg_read;
        u32 bus_clk_rate;
+       u16 clk_divisor_non_hs_mode;
        bool is_suspended;
 };
 
@@ -410,6 +421,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
        u32 val;
        int err = 0;
        u32 clk_divisor;
+       unsigned long timeout = jiffies + HZ;
 
        err = tegra_i2c_clock_enable(i2c_dev);
        if (err < 0) {
@@ -431,7 +443,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 
        /* Make sure clock divisor programmed correctly */
        clk_divisor = i2c_dev->hw->clk_divisor_hs_mode;
-       clk_divisor |= i2c_dev->hw->clk_divisor_std_fast_mode <<
+       clk_divisor |= i2c_dev->clk_divisor_non_hs_mode <<
                                        I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
        i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
 
@@ -451,6 +463,18 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
        if (tegra_i2c_flush_fifos(i2c_dev))
                err = -ETIMEDOUT;
 
+       if (i2c_dev->hw->has_config_load_reg) {
+               i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
+               while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
+                       if (time_after(jiffies, timeout)) {
+                               dev_warn(i2c_dev->dev,
+                                       "timeout waiting for config load\n");
+                               return -ETIMEDOUT;
+                       }
+                       msleep(1);
+               }
+       }
+
        tegra_i2c_clock_disable(i2c_dev);
 
        if (i2c_dev->irq_disabled) {
@@ -656,8 +680,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 static u32 tegra_i2c_func(struct i2c_adapter *adap)
 {
        struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
-       u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
-                               I2C_FUNC_PROTOCOL_MANGLING;
+       u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+                 I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
 
        if (i2c_dev->hw->has_continue_xfer_support)
                ret |= I2C_FUNC_NOSTART;
@@ -669,12 +693,20 @@ static const struct i2c_algorithm tegra_i2c_algo = {
        .functionality  = tegra_i2c_func,
 };
 
+/* payload size is only 12 bit */
+static struct i2c_adapter_quirks tegra_i2c_quirks = {
+       .max_read_len = 4096,
+       .max_write_len = 4096,
+};
+
 static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
        .has_continue_xfer_support = false,
        .has_per_pkt_xfer_complete_irq = false,
        .has_single_clk_source = false,
        .clk_divisor_hs_mode = 3,
        .clk_divisor_std_fast_mode = 0,
+       .clk_divisor_fast_plus_mode = 0,
+       .has_config_load_reg = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -683,6 +715,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,
+       .clk_divisor_fast_plus_mode = 0,
+       .has_config_load_reg = false,
 };
 
 static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -691,10 +725,23 @@ 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,
+       .clk_divisor_fast_plus_mode = 0x10,
+       .has_config_load_reg = false,
+};
+
+static const struct tegra_i2c_hw_feature tegra124_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,
 };
 
 /* Match table for of_platform binding */
 static const struct of_device_id tegra_i2c_of_match[] = {
+       { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_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, },
@@ -739,6 +786,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
        i2c_dev->base = base;
        i2c_dev->div_clk = div_clk;
        i2c_dev->adapter.algo = &tegra_i2c_algo;
+       i2c_dev->adapter.quirks = &tegra_i2c_quirks;
        i2c_dev->irq = irq;
        i2c_dev->cont_id = pdev->id;
        i2c_dev->dev = &pdev->dev;
@@ -786,7 +834,14 @@ static int tegra_i2c_probe(struct platform_device *pdev)
                }
        }
 
-       clk_multiplier *= (i2c_dev->hw->clk_divisor_std_fast_mode + 1);
+       i2c_dev->clk_divisor_non_hs_mode =
+                       i2c_dev->hw->clk_divisor_std_fast_mode;
+       if (i2c_dev->hw->clk_divisor_fast_plus_mode &&
+               (i2c_dev->bus_clk_rate == 1000000))
+               i2c_dev->clk_divisor_non_hs_mode =
+                       i2c_dev->hw->clk_divisor_fast_plus_mode;
+
+       clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
        ret = clk_set_rate(i2c_dev->div_clk,
                           i2c_dev->bus_clk_rate * clk_multiplier);
        if (ret) {
@@ -818,7 +873,6 @@ static int tegra_i2c_probe(struct platform_device *pdev)
        i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
        strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
                sizeof(i2c_dev->adapter.name));
-       i2c_dev->adapter.algo = &tegra_i2c_algo;
        i2c_dev->adapter.dev.parent = &pdev->dev;
        i2c_dev->adapter.nr = pdev->id;
        i2c_dev->adapter.dev.of_node = pdev->dev.of_node;