Commit e32624ef820f821b94333402788e79979681eb29
Committed by
Tom Warren
1 parent
702b872894
Exists in
master
and in
53 other branches
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>
Showing 4 changed files with 45 additions and 5 deletions Side-by-side Diff
arch/arm/include/asm/arch-tegra/tegra_i2c.h
... | ... | @@ -105,6 +105,7 @@ |
105 | 105 | u32 sl_delay_count; /* 3C: I2C_I2C_SL_DELAY_COUNT */ |
106 | 106 | u32 reserved_2[4]; /* 40: */ |
107 | 107 | struct i2c_control control; /* 50 ~ 68 */ |
108 | + u32 clk_div; /* 6C: I2C_I2C_CLOCK_DIVISOR */ | |
108 | 109 | }; |
109 | 110 | |
110 | 111 | /* bit fields definitions for IO Packet Header 1 format */ |
... | ... | @@ -153,6 +154,11 @@ |
153 | 154 | #define I2C_INT_NO_ACK_MASK (1 << I2C_INT_NO_ACK_SHIFT) |
154 | 155 | #define I2C_INT_ARBITRATION_LOST_SHIFT 2 |
155 | 156 | #define I2C_INT_ARBITRATION_LOST_MASK (1 << I2C_INT_ARBITRATION_LOST_SHIFT) |
157 | + | |
158 | +/* I2C_CLK_DIVISOR_REGISTER */ | |
159 | +#define CLK_DIV_STD_FAST_MODE 0x19 | |
160 | +#define CLK_DIV_HS_MODE 1 | |
161 | +#define CLK_MULT_STD_FAST_MODE 8 | |
156 | 162 | |
157 | 163 | /** |
158 | 164 | * Returns the bus number of the DVC controller |
drivers/i2c/tegra_i2c.c
... | ... | @@ -46,6 +46,7 @@ |
46 | 46 | struct i2c_control *control; |
47 | 47 | struct i2c_ctlr *regs; |
48 | 48 | int is_dvc; /* DVC type, rather than I2C */ |
49 | + int is_scs; /* single clock source (T114+) */ | |
49 | 50 | int inited; /* bus is inited */ |
50 | 51 | }; |
51 | 52 | |
52 | 53 | |
... | ... | @@ -88,8 +89,29 @@ |
88 | 89 | * 16 to get the right frequency. |
89 | 90 | */ |
90 | 91 | clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, |
91 | - i2c_bus->speed * 2 * 8); | |
92 | + i2c_bus->speed * 2 * 8); | |
92 | 93 | |
94 | + if (i2c_bus->is_scs) { | |
95 | + /* | |
96 | + * T114 I2C went to a single clock source for standard/fast and | |
97 | + * HS clock speeds. The new clock rate setting calculation is: | |
98 | + * SCL = CLK_SOURCE.I2C / | |
99 | + * (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) * | |
100 | + * I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1). | |
101 | + * | |
102 | + * NOTE: We do this here, after the initial clock/pll start, | |
103 | + * because if we read the clk_div reg before the controller | |
104 | + * is running, we hang, and we need it for the new calc. | |
105 | + */ | |
106 | + int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16; | |
107 | + debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, | |
108 | + clk_div_stdfst_mode); | |
109 | + | |
110 | + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, | |
111 | + CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) * | |
112 | + i2c_bus->speed * 2); | |
113 | + } | |
114 | + | |
93 | 115 | /* Reset I2C controller. */ |
94 | 116 | i2c_reset_controller(i2c_bus); |
95 | 117 | |
96 | 118 | |
... | ... | @@ -352,10 +374,11 @@ |
352 | 374 | * @param node_list list of nodes to process (any <=0 are ignored) |
353 | 375 | * @param count number of nodes to process |
354 | 376 | * @param is_dvc 1 if these are DVC ports, 0 if standard I2C |
377 | + * @param is_scs 1 if this HW uses a single clock source (T114+) | |
355 | 378 | * @return 0 if ok, -1 on error |
356 | 379 | */ |
357 | 380 | static int process_nodes(const void *blob, int node_list[], int count, |
358 | - int is_dvc) | |
381 | + int is_dvc, int is_scs) | |
359 | 382 | { |
360 | 383 | struct i2c_bus *i2c_bus; |
361 | 384 | int i; |
... | ... | @@ -375,6 +398,8 @@ |
375 | 398 | return -1; |
376 | 399 | } |
377 | 400 | |
401 | + i2c_bus->is_scs = is_scs; | |
402 | + | |
378 | 403 | i2c_bus->is_dvc = is_dvc; |
379 | 404 | if (is_dvc) { |
380 | 405 | i2c_bus->control = |
381 | 406 | |
382 | 407 | |
383 | 408 | |
... | ... | @@ -403,18 +428,25 @@ |
403 | 428 | const void *blob = gd->fdt_blob; |
404 | 429 | int count; |
405 | 430 | |
406 | - /* First get the normal i2c ports */ | |
431 | + /* First check for newer (T114+) I2C ports */ | |
407 | 432 | count = fdtdec_find_aliases_for_id(blob, "i2c", |
433 | + COMPAT_NVIDIA_TEGRA114_I2C, node_list, | |
434 | + TEGRA_I2C_NUM_CONTROLLERS); | |
435 | + if (process_nodes(blob, node_list, count, 0, 1)) | |
436 | + return; | |
437 | + | |
438 | + /* Now get the older (T20/T30) normal I2C ports */ | |
439 | + count = fdtdec_find_aliases_for_id(blob, "i2c", | |
408 | 440 | COMPAT_NVIDIA_TEGRA20_I2C, node_list, |
409 | 441 | TEGRA_I2C_NUM_CONTROLLERS); |
410 | - if (process_nodes(blob, node_list, count, 0)) | |
442 | + if (process_nodes(blob, node_list, count, 0, 0)) | |
411 | 443 | return; |
412 | 444 | |
413 | 445 | /* Now look for dvc ports */ |
414 | 446 | count = fdtdec_add_aliases_for_id(blob, "i2c", |
415 | 447 | COMPAT_NVIDIA_TEGRA20_DVC, node_list, |
416 | 448 | TEGRA_I2C_NUM_CONTROLLERS); |
417 | - if (process_nodes(blob, node_list, count, 1)) | |
449 | + if (process_nodes(blob, node_list, count, 1, 0)) | |
418 | 450 | return; |
419 | 451 | } |
420 | 452 |
include/fdtdec.h
... | ... | @@ -62,6 +62,7 @@ |
62 | 62 | enum fdt_compat_id { |
63 | 63 | COMPAT_UNKNOWN, |
64 | 64 | COMPAT_NVIDIA_TEGRA20_USB, /* Tegra20 USB port */ |
65 | + COMPAT_NVIDIA_TEGRA114_I2C, /* Tegra114 I2C w/single clock source */ | |
65 | 66 | COMPAT_NVIDIA_TEGRA20_I2C, /* Tegra20 i2c */ |
66 | 67 | COMPAT_NVIDIA_TEGRA20_DVC, /* Tegra20 dvc (really just i2c) */ |
67 | 68 | COMPAT_NVIDIA_TEGRA20_EMC, /* Tegra20 memory controller */ |
lib/fdtdec.c
... | ... | @@ -37,6 +37,7 @@ |
37 | 37 | static const char * const compat_names[COMPAT_COUNT] = { |
38 | 38 | COMPAT(UNKNOWN, "<none>"), |
39 | 39 | COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"), |
40 | + COMPAT(NVIDIA_TEGRA114_I2C, "nvidia,tegra114-i2c"), | |
40 | 41 | COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"), |
41 | 42 | COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"), |
42 | 43 | COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"), |