diff options
author | Tom Warren <twarren.nvidia@gmail.com> | 2013-02-08 07:25:30 (GMT) |
---|---|---|
committer | Tom Warren <twarren@nvidia.com> | 2013-03-14 18:06:41 (GMT) |
commit | e32624ef820f821b94333402788e79979681eb29 (patch) | |
tree | d328b83fe643568c319bd46c1c9458317f46ad3b /drivers | |
parent | 702b87289438cfc3165408f0eaf999b0b67c1e7e (diff) | |
download | u-boot-e32624ef820f821b94333402788e79979681eb29.tar.xz |
Tegra: I2C: Add T114 clock support to tegra_i2c driver
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/tegra_i2c.c | 42 |
1 files changed, 37 insertions, 5 deletions
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index efc77fa..ca71cd3 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -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; } |