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>
This commit is contained in:
Tom Warren 2013-02-08 07:25:30 +00:00 committed by Tom Warren
parent 702b872894
commit e32624ef82
4 changed files with 45 additions and 5 deletions

View file

@ -105,6 +105,7 @@ struct i2c_ctlr {
u32 sl_delay_count; /* 3C: I2C_I2C_SL_DELAY_COUNT */ u32 sl_delay_count; /* 3C: I2C_I2C_SL_DELAY_COUNT */
u32 reserved_2[4]; /* 40: */ u32 reserved_2[4]; /* 40: */
struct i2c_control control; /* 50 ~ 68 */ struct i2c_control control; /* 50 ~ 68 */
u32 clk_div; /* 6C: I2C_I2C_CLOCK_DIVISOR */
}; };
/* bit fields definitions for IO Packet Header 1 format */ /* 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_SHIFT 2
#define I2C_INT_ARBITRATION_LOST_MASK (1 << I2C_INT_ARBITRATION_LOST_SHIFT) #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 * Returns the bus number of the DVC controller
* *

View file

@ -46,6 +46,7 @@ struct i2c_bus {
struct i2c_control *control; struct i2c_control *control;
struct i2c_ctlr *regs; struct i2c_ctlr *regs;
int is_dvc; /* DVC type, rather than I2C */ int is_dvc; /* DVC type, rather than I2C */
int is_scs; /* single clock source (T114+) */
int inited; /* bus is inited */ 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. * 16 to get the right frequency.
*/ */
clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, 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. */ /* Reset I2C controller. */
i2c_reset_controller(i2c_bus); 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 node_list list of nodes to process (any <=0 are ignored)
* @param count number of nodes to process * @param count number of nodes to process
* @param is_dvc 1 if these are DVC ports, 0 if standard I2C * @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 * @return 0 if ok, -1 on error
*/ */
static int process_nodes(const void *blob, int node_list[], int count, 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; struct i2c_bus *i2c_bus;
int i; int i;
@ -375,6 +398,8 @@ static int process_nodes(const void *blob, int node_list[], int count,
return -1; return -1;
} }
i2c_bus->is_scs = is_scs;
i2c_bus->is_dvc = is_dvc; i2c_bus->is_dvc = is_dvc;
if (is_dvc) { if (is_dvc) {
i2c_bus->control = i2c_bus->control =
@ -403,18 +428,25 @@ void i2c_init_board(void)
const void *blob = gd->fdt_blob; const void *blob = gd->fdt_blob;
int count; 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", count = fdtdec_find_aliases_for_id(blob, "i2c",
COMPAT_NVIDIA_TEGRA20_I2C, node_list, COMPAT_NVIDIA_TEGRA20_I2C, node_list,
TEGRA_I2C_NUM_CONTROLLERS); TEGRA_I2C_NUM_CONTROLLERS);
if (process_nodes(blob, node_list, count, 0)) if (process_nodes(blob, node_list, count, 0, 0))
return; return;
/* Now look for dvc ports */ /* Now look for dvc ports */
count = fdtdec_add_aliases_for_id(blob, "i2c", count = fdtdec_add_aliases_for_id(blob, "i2c",
COMPAT_NVIDIA_TEGRA20_DVC, node_list, COMPAT_NVIDIA_TEGRA20_DVC, node_list,
TEGRA_I2C_NUM_CONTROLLERS); TEGRA_I2C_NUM_CONTROLLERS);
if (process_nodes(blob, node_list, count, 1)) if (process_nodes(blob, node_list, count, 1, 0))
return; return;
} }

View file

@ -62,6 +62,7 @@ struct fdt_memory {
enum fdt_compat_id { enum fdt_compat_id {
COMPAT_UNKNOWN, COMPAT_UNKNOWN,
COMPAT_NVIDIA_TEGRA20_USB, /* Tegra20 USB port */ 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_I2C, /* Tegra20 i2c */
COMPAT_NVIDIA_TEGRA20_DVC, /* Tegra20 dvc (really just i2c) */ COMPAT_NVIDIA_TEGRA20_DVC, /* Tegra20 dvc (really just i2c) */
COMPAT_NVIDIA_TEGRA20_EMC, /* Tegra20 memory controller */ COMPAT_NVIDIA_TEGRA20_EMC, /* Tegra20 memory controller */

View file

@ -37,6 +37,7 @@ DECLARE_GLOBAL_DATA_PTR;
static const char * const compat_names[COMPAT_COUNT] = { static const char * const compat_names[COMPAT_COUNT] = {
COMPAT(UNKNOWN, "<none>"), COMPAT(UNKNOWN, "<none>"),
COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"), COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"),
COMPAT(NVIDIA_TEGRA114_I2C, "nvidia,tegra114-i2c"),
COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"), COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"),
COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"), COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"),
COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"), COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"),