Tegra: I2C: Add T114 clock support to tegra_i2c driver
authorTom Warren <twarren.nvidia@gmail.com>
Fri, 8 Feb 2013 07:25:30 +0000 (07:25 +0000)
committerTom Warren <twarren@nvidia.com>
Thu, 14 Mar 2013 18:06:41 +0000 (11:06 -0700)
T114 has a slightly different I2C clock, with a new (extra) divisor
in standard/fast mode and HS mode. Tested on my Dalmore, and the I2C
clock is 100KHz +/- 3Hz on my Saleae Logic analyzer.

Added a new entry in compat_names for T114 I2C since it differs
from the previous Tegra SoCs. A flag is set when T114 I2C HW is
found so new features like the extra clock divisor can be used.

Signed-off-by: Tom Warren <twarren@nvidia.com>
Acked-by: Laxman Dewangan <ldewangan@nvidia.com>
arch/arm/include/asm/arch-tegra/tegra_i2c.h
drivers/i2c/tegra_i2c.c
include/fdtdec.h
lib/fdtdec.c

index 26507447185f8285d61ddc56508be6f7d6a66f9e..853e59bb6e65446eb74d148f87aac399fa3e8708 100644 (file)
@@ -105,6 +105,7 @@ struct i2c_ctlr {
        u32 sl_delay_count;             /* 3C: I2C_I2C_SL_DELAY_COUNT */
        u32 reserved_2[4];              /* 40: */
        struct i2c_control control;     /* 50 ~ 68 */
+       u32 clk_div;                    /* 6C: I2C_I2C_CLOCK_DIVISOR */
 };
 
 /* bit fields definitions for IO Packet Header 1 format */
@@ -154,6 +155,11 @@ struct i2c_ctlr {
 #define I2C_INT_ARBITRATION_LOST_SHIFT 2
 #define I2C_INT_ARBITRATION_LOST_MASK  (1 << I2C_INT_ARBITRATION_LOST_SHIFT)
 
+/* I2C_CLK_DIVISOR_REGISTER */
+#define CLK_DIV_STD_FAST_MODE          0x19
+#define CLK_DIV_HS_MODE                        1
+#define CLK_MULT_STD_FAST_MODE         8
+
 /**
  * Returns the bus number of the DVC controller
  *
index efc77fa910fcc7dcb969afc99d5c5822b99096f1..ca71cd3ee4ce60dd48dc23363cdab67d3c042736 100644 (file)
@@ -46,6 +46,7 @@ struct i2c_bus {
        struct i2c_control      *control;
        struct i2c_ctlr         *regs;
        int                     is_dvc; /* DVC type, rather than I2C */
+       int                     is_scs; /* single clock source (T114+) */
        int                     inited; /* bus is inited */
 };
 
@@ -88,7 +89,28 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus)
         * 16 to get the right frequency.
         */
        clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
-                              i2c_bus->speed * 2 * 8);
+               i2c_bus->speed * 2 * 8);
+
+       if (i2c_bus->is_scs) {
+               /*
+                * T114 I2C went to a single clock source for standard/fast and
+                * HS clock speeds. The new clock rate setting calculation is:
+                *  SCL = CLK_SOURCE.I2C /
+                *   (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) *
+                *   I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1).
+                *
+                * NOTE: We do this here, after the initial clock/pll start,
+                * because if we read the clk_div reg before the controller
+                * is running, we hang, and we need it for the new calc.
+                */
+               int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16;
+               debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__,
+                       clk_div_stdfst_mode);
+
+               clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
+                       CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) *
+                       i2c_bus->speed * 2);
+       }
 
        /* Reset I2C controller. */
        i2c_reset_controller(i2c_bus);
@@ -352,10 +374,11 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus)
  * @param node_list    list of nodes to process (any <=0 are ignored)
  * @param count                number of nodes to process
  * @param is_dvc       1 if these are DVC ports, 0 if standard I2C
+ * @param is_scs       1 if this HW uses a single clock source (T114+)
  * @return 0 if ok, -1 on error
  */
 static int process_nodes(const void *blob, int node_list[], int count,
-                        int is_dvc)
+                        int is_dvc, int is_scs)
 {
        struct i2c_bus *i2c_bus;
        int i;
@@ -375,6 +398,8 @@ static int process_nodes(const void *blob, int node_list[], int count,
                        return -1;
                }
 
+               i2c_bus->is_scs = is_scs;
+
                i2c_bus->is_dvc = is_dvc;
                if (is_dvc) {
                        i2c_bus->control =
@@ -403,18 +428,25 @@ void i2c_init_board(void)
        const void *blob = gd->fdt_blob;
        int count;
 
-       /* First get the normal i2c ports */
+       /* First check for newer (T114+) I2C ports */
+       count = fdtdec_find_aliases_for_id(blob, "i2c",
+                       COMPAT_NVIDIA_TEGRA114_I2C, node_list,
+                       TEGRA_I2C_NUM_CONTROLLERS);
+       if (process_nodes(blob, node_list, count, 0, 1))
+               return;
+
+       /* Now get the older (T20/T30) normal I2C ports */
        count = fdtdec_find_aliases_for_id(blob, "i2c",
                        COMPAT_NVIDIA_TEGRA20_I2C, node_list,
                        TEGRA_I2C_NUM_CONTROLLERS);
-       if (process_nodes(blob, node_list, count, 0))
+       if (process_nodes(blob, node_list, count, 0, 0))
                return;
 
        /* Now look for dvc ports */
        count = fdtdec_add_aliases_for_id(blob, "i2c",
                        COMPAT_NVIDIA_TEGRA20_DVC, node_list,
                        TEGRA_I2C_NUM_CONTROLLERS);
-       if (process_nodes(blob, node_list, count, 1))
+       if (process_nodes(blob, node_list, count, 1, 0))
                return;
 }
 
index 77f244f4171106f1eed6234a0c4b2409eebfb0bc..c7a92d553edb5f8940ad6470ab2b6dc2a4747cf1 100644 (file)
@@ -62,6 +62,7 @@ struct fdt_memory {
 enum fdt_compat_id {
        COMPAT_UNKNOWN,
        COMPAT_NVIDIA_TEGRA20_USB,      /* Tegra20 USB port */
+       COMPAT_NVIDIA_TEGRA114_I2C,     /* Tegra114 I2C w/single clock source */
        COMPAT_NVIDIA_TEGRA20_I2C,      /* Tegra20 i2c */
        COMPAT_NVIDIA_TEGRA20_DVC,      /* Tegra20 dvc (really just i2c) */
        COMPAT_NVIDIA_TEGRA20_EMC,      /* Tegra20 memory controller */
index 3ae348dd307bc64aa2ffa8a95d0177d98da7d55b..7c7fc833a60dd93526916b6b202ea83a5a2ad539 100644 (file)
@@ -37,6 +37,7 @@ DECLARE_GLOBAL_DATA_PTR;
 static const char * const compat_names[COMPAT_COUNT] = {
        COMPAT(UNKNOWN, "<none>"),
        COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"),
+       COMPAT(NVIDIA_TEGRA114_I2C, "nvidia,tegra114-i2c"),
        COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"),
        COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"),
        COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"),