[U-Boot] [PATCH v3 0/9] tegra: Add I2C driver and associated parts

This series brings in an I2C driver for Tegra which can be configured by a flat device tree.
It supports 8- and 16-bit addresses and both the normal I2C ports and the DVC port (for controlling the power management unit (PMU)).
Recent Linux bindings are used, based on example .dts files found in branch for-3.3/dt at:
git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra.git
(I could not find the actual binding documentation to include here)
Note: There is still an open question on I2C2 pinmux. The idea of an 'nvidia,pinmux-config' settings did not find favour, so we have no way of passing the correct config to funcmux for this port. Therefore we have disabled it for now. The impact on Seaboard is that the TPM and battery charger cannot be accessed.
I am sending this series to the linux-tegra list also, and will do so for future series also. This is because anything to do with fdt also affects the kernel bindings. Please advise if this is not useful.
Changes in v2: - Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined - Adjust definitions to fit new peripheral clock bindings - Change 'speed' to 'clock-frequency' - Change i2c array to static - Disable port 2 as it is not used - Make i2c/dvc decision come from fdt - Remove i2c configuring using CONFIG (use fdt instead) - Remove u-boot,pinmux binding (sadly) - Return an error if an unavailable i2c bus is selected - Simplify code in i2c_addr_ok() - Tidy comment style - Use DIV_ROUND_UP() instead of a home-grown macro - Use new fdtdec alias decode function
Changes in v3: - Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports - Add comment on how to select pinmux for I2C2 - Add comments and warning for mixed alias use in fdtdec - Add new patch to support 16-bit clock divisors required by I2C - Change i2c_init() to reset speed and override the fdt setting - Fix i2c_addr_ok() function to check addresses correctly - Move speed setting from tegra20.dtsi to board .dts file - Rename driver to tegra_i2c since it will later be shared with Tegra3 - Tidy up I2C speed selection to use the peripheral clock - Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
Simon Glass (8): tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE fdt: Add function to allow aliases to refer to multiple nodes tegra: Enhance clock support to handle 16-bit clock divisors tegra: fdt: i2c: Add extra I2C bindings for U-Boot tegra: i2c: Initialise I2C on Nvidia boards tegra: i2c: Select I2C ordering for Seaboard tegra: i2c: Select number of controllers for Tegra2 boards tegra: i2c: Enable I2C on Seaboard
Yen Lin (1): tegra: i2c: Add I2C driver
arch/arm/cpu/armv7/tegra2/ap20.c | 10 +- arch/arm/cpu/armv7/tegra2/board.c | 2 +- arch/arm/cpu/armv7/tegra2/clock.c | 63 ++- arch/arm/dts/tegra20.dtsi | 6 +- arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 +- arch/arm/include/asm/arch-tegra2/tegra2.h | 4 +- arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ board/nvidia/common/board.c | 7 + board/nvidia/dts/tegra2-seaboard.dts | 21 + drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566 ++++++++++++++++++++++++++ include/configs/seaboard.h | 8 + include/configs/tegra2-common.h | 3 + include/fdtdec.h | 25 ++ lib/fdtdec.c | 24 +- 15 files changed, 873 insertions(+), 37 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c

Change this name to fit with the current convention in the Tegra header file.
Signed-off-by: Simon Glass sjg@chromium.org Acked-by: Stephen Warren swarren@nvidia.com ---
arch/arm/cpu/armv7/tegra2/ap20.c | 10 +++++----- arch/arm/cpu/armv7/tegra2/board.c | 2 +- arch/arm/include/asm/arch-tegra2/tegra2.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index 4c44bb3..3ea2e26 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -105,14 +105,14 @@ static void enable_cpu_clock(int enable)
static int is_cpu_powered(void) { - struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE;
return (readl(&pmc->pmc_pwrgate_status) & CPU_PWRED) ? 1 : 0; }
static void remove_cpu_io_clamps(void) { - struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; u32 reg;
/* Remove the clamps on the CPU I/O signals */ @@ -126,7 +126,7 @@ static void remove_cpu_io_clamps(void)
static void powerup_cpu(void) { - struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; u32 reg; int timeout = IO_STABILIZATION_DELAY;
@@ -157,7 +157,7 @@ static void powerup_cpu(void)
static void enable_cpu_power_rail(void) { - struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; u32 reg;
reg = readl(&pmc->pmc_cntrl); @@ -277,7 +277,7 @@ void enable_scu(void)
void init_pmc_scratch(void) { - struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *const pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; int i;
/* SCRATCH0 is initialized by the boot ROM and shouldn't be cleared */ diff --git a/arch/arm/cpu/armv7/tegra2/board.c b/arch/arm/cpu/armv7/tegra2/board.c index 410d0bd..77a627d 100644 --- a/arch/arm/cpu/armv7/tegra2/board.c +++ b/arch/arm/cpu/armv7/tegra2/board.c @@ -47,7 +47,7 @@ enum {
unsigned int query_sdram_size(void) { - struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct pmc_ctlr *const pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; u32 reg;
reg = readl(&pmc->pmc_scratch20); diff --git a/arch/arm/include/asm/arch-tegra2/tegra2.h b/arch/arm/include/asm/arch-tegra2/tegra2.h index baae2eb..ca1881e 100644 --- a/arch/arm/include/asm/arch-tegra2/tegra2.h +++ b/arch/arm/include/asm/arch-tegra2/tegra2.h @@ -39,7 +39,7 @@ #define NV_PA_APB_UARTD_BASE (NV_PA_APB_MISC_BASE + 0x6300) #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400) #define TEGRA2_SPI_BASE (NV_PA_APB_MISC_BASE + 0xC380) -#define NV_PA_PMC_BASE 0x7000E400 +#define TEGRA2_PMC_BASE (NV_PA_APB_MISC_BASE + 0xE400) #define NV_PA_CSITE_BASE 0x70040000 #define TEGRA_USB1_BASE 0xC5000000 #define TEGRA_USB3_BASE 0xC5008000 @@ -55,7 +55,7 @@ struct timerus { unsigned int cntr_1us; }; #else /* __ASSEMBLY__ */ -#define PRM_RSTCTRL NV_PA_PMC_BASE +#define PRM_RSTCTRL TEGRA2_PMC_BASE #endif
#endif /* TEGRA2_H */

Some devices can deal with multiple compatible properties. The devices need to know which nodes to bind to which features. For example an I2C driver which supports two different controller types will want to know which type it is dealing with in each case.
The new fdtdec_add_aliases_for_id() function deals with this by allowing the driver to search for additional compatible nodes for a different ID. It can then detect the new ones and perform appropriate processing.
Another option considered was to return a tuple (node offset, compat id) and have the function be passed a list of compatible IDs. This is more overhead for the common case though. We may add such a function later if more drivers in U-Boot require it.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v3: - Add comments and warning for mixed alias use in fdtdec
include/fdtdec.h | 23 +++++++++++++++++++++++ lib/fdtdec.c | 22 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/include/fdtdec.h b/include/fdtdec.h index bd2222c..b028bfb 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -200,6 +200,29 @@ int fdtdec_find_aliases_for_id(const void *blob, const char *name, enum fdt_compat_id id, int *node_list, int maxcount);
/* + * This function is similar to fdtdec_find_aliases_for_id() except that it + * adds to the node_list that is passed in. Any 0 elements are considered + * available for allocation - others are considered already used and are + * skipped. + * + * You can use this by calling fdtdec_find_aliases_for_id() with an + * uninitialised array, then setting the elements that are returned to -1, + * say, then calling this function, perhaps with a different compat id. + * Any elements you get back that are >0 are new nodes added by the call + * to this function. + * + * Note that if you have some nodes with aliases and some without, you are + * sailing close to the wind. The call to fdtdec_find_aliases_for_id() with + * one compat_id may fill in positions for which you have aliases defined + * for another compat_id. When you later call *this* function with the second + * compat_id, the alias positions may already be used. A debug warning may + * be generated in this case, but it is safest to define aliases for all + * nodes when you care about the ordering. + */ +int fdtdec_add_aliases_for_id(const void *blob, const char *name, + enum fdt_compat_id id, int *node_list, int maxcount); + +/* * Get the name for a compatible ID * * @param id Compatible ID to look for diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 7260c73..35361d1 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -151,10 +151,18 @@ int fdtdec_next_alias(const void *blob, const char *name, return node; }
-/* TODO: Can we tighten this code up a little? */ int fdtdec_find_aliases_for_id(const void *blob, const char *name, enum fdt_compat_id id, int *node_list, int maxcount) { + memset(node_list, '\0', sizeof(*node_list) * maxcount); + + return fdtdec_add_aliases_for_id(blob, name, id, node_list, maxcount); +} + +/* TODO: Can we tighten this code up a little? */ +int fdtdec_add_aliases_for_id(const void *blob, const char *name, + enum fdt_compat_id id, int *node_list, int maxcount) +{ int name_len = strlen(name); int nodes[maxcount]; int num_found = 0; @@ -184,8 +192,6 @@ int fdtdec_find_aliases_for_id(const void *blob, const char *name, __func__, name);
/* Now find all the aliases */ - memset(node_list, '\0', sizeof(*node_list) * maxcount); - for (offset = fdt_first_property_offset(blob, alias_node); offset > 0; offset = fdt_next_property_offset(blob, offset)) { @@ -232,11 +238,19 @@ int fdtdec_find_aliases_for_id(const void *blob, const char *name, * it as done. */ if (fdtdec_get_is_enabled(blob, node)) { + if (node_list[number]) { + debug("%s: warning: alias '%s' requires that " + "a node be placed in the list in a " + "position which is already filled by " + "node '%s'\n", __func__, path, + fdt_get_name(blob, node, NULL)); + continue; + } node_list[number] = node; if (number >= num_found) num_found = number + 1; } - nodes[j] = 0; + nodes[found] = 0; }
/* Add any nodes not mentioned by an alias */

I2C ports have a 16-bit clock divisor. Add code to handle this special case so that I2C speeds below 150KHz are supported.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v3: - Add new patch to support 16-bit clock divisors required by I2C
arch/arm/cpu/armv7/tegra2/clock.c | 63 ++++++++++++++++++---------- arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 ++++- 2 files changed, 49 insertions(+), 24 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 11d2346..e58cb3b 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -67,6 +67,7 @@ enum clock_type_id { CLOCK_TYPE_MCPT, CLOCK_TYPE_PCM, CLOCK_TYPE_PCMT, + CLOCK_TYPE_PCMT16, /* CLOCK_TYPE_PCMT with 16-bit divider */ CLOCK_TYPE_PCXTS, CLOCK_TYPE_PDCT,
@@ -98,6 +99,7 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = { { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) }, + { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) }, { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) }, }; @@ -211,8 +213,8 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
/* 0x08 */ TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT), - TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT), - TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT16), + TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT), @@ -246,7 +248,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT), - TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT),
/* 0x28 */ @@ -256,7 +258,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT), - TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT),
/* 0x30 */ @@ -518,14 +520,16 @@ void clock_ll_set_source(enum periph_id periph_id, unsigned source) * Given the parent's rate and the required rate for the children, this works * out the peripheral clock divider to use, in 7.1 binary format. * + * @param divider_bits number of divider bits (8 or 16) * @param parent_rate clock rate of parent clock in Hz * @param rate required clock rate for this clock * @return divider which should be used */ -static int clk_div7_1_get_divider(unsigned long parent_rate, - unsigned long rate) +static int clk_get_divider(unsigned divider_bits, unsigned long parent_rate, + unsigned long rate) { u64 divider = parent_rate * 2; + unsigned max_divider = 1 << divider_bits;
divider += rate - 1; do_div(divider, rate); @@ -533,7 +537,7 @@ static int clk_div7_1_get_divider(unsigned long parent_rate, if ((s64)divider - 2 < 0) return 0;
- if ((s64)divider - 2 > 255) + if ((s64)divider - 2 >= max_divider) return -1;
return divider - 2; @@ -571,6 +575,7 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, * required child clock rate. This function assumes that a second-stage * divisor is available which can divide by powers of 2 from 1 to 256. * + * @param divider_bits number of divider bits (8 or 16) * @param parent_rate clock rate of parent clock in Hz * @param rate required clock rate for this clock * @param extra_div value for the second-stage divisor (not set if this @@ -578,8 +583,8 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, * @return divider which should be used, or -1 if nothing is valid * */ -static int find_best_divider(unsigned long parent_rate, unsigned long rate, - int *extra_div) +static int find_best_divider(unsigned divider_bits, unsigned long parent_rate, + unsigned long rate, int *extra_div) { int shift; int best_divider = -1; @@ -588,7 +593,8 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, /* try dividers from 1 to 256 and find closest match */ for (shift = 0; shift <= 8 && best_error > 0; shift++) { unsigned divided_parent = parent_rate >> shift; - int divider = clk_div7_1_get_divider(divided_parent, rate); + int divider = clk_get_divider(divider_bits, divided_parent, + rate); unsigned effective_rate = get_rate_from_divider(divided_parent, divider); int error = rate - effective_rate; @@ -614,10 +620,11 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, * @param periph_id peripheral to start * @param source PLL id of required parent clock * @param mux_bits Set to number of bits in mux register: 2 or 4 + * @param divider_bits Set to number of divider bits (8 or 16) * @return mux value (0-4, or -1 if not found) */ static int get_periph_clock_source(enum periph_id periph_id, - enum clock_id parent, int *mux_bits) + enum clock_id parent, int *mux_bits, int *divider_bits) { enum clock_type_id type; enum periphc_internal_id internal_id; @@ -631,11 +638,18 @@ static int get_periph_clock_source(enum periph_id periph_id, type = clock_periph_type[internal_id]; assert(clock_type_id_isvalid(type));
- /* Special case here for the clock with a 4-bit source mux */ + /* + * Special cases here for the clock with a 4-bit source mux and I2C + * with its 16-bit divisor + */ if (type == CLOCK_TYPE_PCXTS) *mux_bits = 4; else *mux_bits = 2; + if (type == CLOCK_TYPE_PCMT16) + *divider_bits = 16; + else + *divider_bits = 8;
for (mux = 0; mux < CLOCK_MAX_MUX; mux++) if (clock_source[type][mux] == parent) @@ -661,24 +675,22 @@ static int get_periph_clock_source(enum periph_id periph_id, * Adjust peripheral PLL to use the given divider and source. * * @param periph_id peripheral to adjust - * @param parent Required parent clock (for source mux) - * @param divider Required divider in 7.1 format + * @param source Source number (0-3 or 0-7) + * @param mux_bits Number of mux bits (2 or 4) + * @param divider Required divider in 7.1 or 15.1 format * @return 0 if ok, -1 on error (requesting a parent clock which is not valid * for this peripheral) */ -static int adjust_periph_pll(enum periph_id periph_id, - enum clock_id parent, unsigned divider) +static int adjust_periph_pll(enum periph_id periph_id, int source, + int mux_bits, unsigned divider) { u32 *reg = get_periph_source_reg(periph_id); - unsigned source; - int mux_bits;
clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK, divider << OUT_CLK_DIVISOR_SHIFT); udelay(1);
/* work out the source clock and set it */ - source = get_periph_clock_source(periph_id, parent, &mux_bits); if (source < 0) return -1; if (mux_bits == 4) { @@ -696,14 +708,21 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, enum clock_id parent, unsigned rate, int *extra_div) { unsigned effective_rate; + int mux_bits, divider_bits, source; int divider;
+ /* work out the source clock and set it */ + source = get_periph_clock_source(periph_id, parent, &mux_bits, + ÷r_bits); + if (extra_div) - divider = find_best_divider(pll_rate[parent], rate, extra_div); + divider = find_best_divider(divider_bits, pll_rate[parent], + rate, extra_div); else - divider = clk_div7_1_get_divider(pll_rate[parent], rate); + divider = clk_get_divider(divider_bits, pll_rate[parent], + rate); assert(divider >= 0); - if (adjust_periph_pll(periph_id, parent, divider)) + if (adjust_periph_pll(periph_id, source, mux_bits, divider)) return -1U; debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, get_periph_source_reg(periph_id), diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h b/arch/arm/include/asm/arch-tegra2/clk_rst.h index 0b6e004..415e420 100644 --- a/arch/arm/include/asm/arch-tegra2/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h @@ -125,9 +125,15 @@ struct clk_rst_ctlr { #define OSC_FREQ_SHIFT 30 #define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT)
-/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */ +/* + * CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 - the mask here is normally 8 bits + * but can be 16. We could use knowledge we have to restrict the mask in + * the 8-bit cases (the divider_bits value returned by + * get_periph_clock_source()) but it does not seem worth it since the code + * already checks the ranges of values it is writing, in clk_get_divider(). + */ #define OUT_CLK_DIVISOR_SHIFT 0 -#define OUT_CLK_DIVISOR_MASK (255 << OUT_CLK_DIVISOR_SHIFT) +#define OUT_CLK_DIVISOR_MASK (0xffff << OUT_CLK_DIVISOR_SHIFT)
#define OUT_CLK_SOURCE_SHIFT 30 #define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT)

Simon,
-----Original Message----- From: Simon Glass [mailto:sjg@chromium.org] Sent: Friday, February 03, 2012 6:14 PM To: U-Boot Mailing List Cc: Tom Warren; Stephen Warren; linux-tegra@vger.kernel.org; Simon Glass Subject: [PATCH v3 3/9] tegra: Enhance clock support to handle 16-bit clock divisors
I2C ports have a 16-bit clock divisor. Add code to handle this special case so that I2C speeds below 150KHz are supported.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Add new patch to support 16-bit clock divisors required by I2C
arch/arm/cpu/armv7/tegra2/clock.c | 63 ++++++++++++++++++-------
arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 ++++- 2 files changed, 49 insertions(+), 24 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 11d2346..e58cb3b 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -67,6 +67,7 @@ enum clock_type_id { CLOCK_TYPE_MCPT, CLOCK_TYPE_PCM, CLOCK_TYPE_PCMT,
- CLOCK_TYPE_PCMT16, /* CLOCK_TYPE_PCMT with 16-bit divider */ CLOCK_TYPE_PCXTS, CLOCK_TYPE_PDCT,
@@ -98,6 +99,7 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = { { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) },
- { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) }, { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) },
}; @@ -211,8 +213,8 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
/* 0x08 */ TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT16),
- TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT),
@@ -246,7 +248,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT),
- TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT),
/* 0x28 */
@@ -256,7 +258,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT),
/* 0x30 */
@@ -518,14 +520,16 @@ void clock_ll_set_source(enum periph_id periph_id, unsigned source)
- Given the parent's rate and the required rate for the children, this
works
- out the peripheral clock divider to use, in 7.1 binary format.
*/
- @param divider_bits number of divider bits (8 or 16)
- @param parent_rate clock rate of parent clock in Hz
- @param rate required clock rate for this clock
- @return divider which should be used
-static int clk_div7_1_get_divider(unsigned long parent_rate,
unsigned long rate)
+static int clk_get_divider(unsigned divider_bits, unsigned long parent_rate,
unsigned long rate)
{ u64 divider = parent_rate * 2;
unsigned max_divider = 1 << divider_bits;
divider += rate - 1; do_div(divider, rate);
@@ -533,7 +537,7 @@ static int clk_div7_1_get_divider(unsigned long parent_rate, if ((s64)divider - 2 < 0) return 0;
- if ((s64)divider - 2 > 255)
if ((s64)divider - 2 >= max_divider) return -1;
return divider - 2;
@@ -571,6 +575,7 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
- required child clock rate. This function assumes that a second-stage
- divisor is available which can divide by powers of 2 from 1 to 256.
- @param divider_bits number of divider bits (8 or 16)
- @param parent_rate clock rate of parent clock in Hz
- @param rate required clock rate for this clock
- @param extra_div value for the second-stage divisor (not set if
this @@ -578,8 +583,8 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
- @return divider which should be used, or -1 if nothing is valid
*/ -static int find_best_divider(unsigned long parent_rate, unsigned long rate,
int *extra_div)
+static int find_best_divider(unsigned divider_bits, unsigned long parent_rate,
unsigned long rate, int *extra_div)
{ int shift; int best_divider = -1; @@ -588,7 +593,8 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, /* try dividers from 1 to 256 and find closest match */ for (shift = 0; shift <= 8 && best_error > 0; shift++) { unsigned divided_parent = parent_rate >> shift;
int divider = clk_div7_1_get_divider(divided_parent, rate);
int divider = clk_get_divider(divider_bits, divided_parent,
unsigned effective_rate = get_rate_from_divider(divided_parent, divider); int error = rate - effective_rate;rate);
@@ -614,10 +620,11 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate,
- @param periph_id peripheral to start
- @param source PLL id of required parent clock
- @param mux_bits Set to number of bits in mux register: 2 or 4
*/
- @param divider_bits Set to number of divider bits (8 or 16)
- @return mux value (0-4, or -1 if not found)
static int get_periph_clock_source(enum periph_id periph_id,
enum clock_id parent, int *mux_bits)
enum clock_id parent, int *mux_bits, int *divider_bits)
{ enum clock_type_id type; enum periphc_internal_id internal_id; @@ -631,11 +638,18 @@ static int get_periph_clock_source(enum periph_id periph_id, type = clock_periph_type[internal_id]; assert(clock_type_id_isvalid(type));
- /* Special case here for the clock with a 4-bit source mux */
/*
* Special cases here for the clock with a 4-bit source mux and I2C
* with its 16-bit divisor
*/
if (type == CLOCK_TYPE_PCXTS) *mux_bits = 4; else *mux_bits = 2;
if (type == CLOCK_TYPE_PCMT16)
*divider_bits = 16;
else
*divider_bits = 8;
for (mux = 0; mux < CLOCK_MAX_MUX; mux++) if (clock_source[type][mux] == parent) @@ -661,24 +675,22 @@
static int get_periph_clock_source(enum periph_id periph_id,
- Adjust peripheral PLL to use the given divider and source.
- @param periph_id peripheral to adjust
- @param parent Required parent clock (for source mux)
- @param divider Required divider in 7.1 format
- @param source Source number (0-3 or 0-7)
- @param mux_bits Number of mux bits (2 or 4)
- @param divider Required divider in 7.1 or 15.1 format
- @return 0 if ok, -1 on error (requesting a parent clock which is not
valid
for this peripheral)
*/ -static int adjust_periph_pll(enum periph_id periph_id,
enum clock_id parent, unsigned divider)
+static int adjust_periph_pll(enum periph_id periph_id, int source,
int mux_bits, unsigned divider)
{ u32 *reg = get_periph_source_reg(periph_id);
unsigned source;
int mux_bits;
clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK, divider << OUT_CLK_DIVISOR_SHIFT); udelay(1);
/* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits); if (source < 0) return -1; if (mux_bits == 4) {
@@ -696,14 +708,21 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, enum clock_id parent, unsigned rate, int *extra_div) { unsigned effective_rate;
int mux_bits, divider_bits, source; int divider;
/* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits,
÷r_bits);
if (extra_div)
divider = find_best_divider(pll_rate[parent], rate, extra_div);
divider = find_best_divider(divider_bits, pll_rate[parent],
elserate, extra_div);
divider = clk_div7_1_get_divider(pll_rate[parent], rate);
divider = clk_get_divider(divider_bits, pll_rate[parent],
assert(divider >= 0);rate);
- if (adjust_periph_pll(periph_id, parent, divider))
- if (adjust_periph_pll(periph_id, source, mux_bits, divider)) return -1U; debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, get_periph_source_reg(periph_id),
diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h b/arch/arm/include/asm/arch-tegra2/clk_rst.h index 0b6e004..415e420 100644 --- a/arch/arm/include/asm/arch-tegra2/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h @@ -125,9 +125,15 @@ struct clk_rst_ctlr { #define OSC_FREQ_SHIFT 30 #define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT)
-/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */ +/*
- CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 - the mask here is normally 8
+bits
- but can be 16. We could use knowledge we have to restrict the mask
+in
- the 8-bit cases (the divider_bits value returned by
- get_periph_clock_source()) but it does not seem worth it since the
+code
- already checks the ranges of values it is writing, in clk_get_divider().
- */
#define OUT_CLK_DIVISOR_SHIFT 0 -#define OUT_CLK_DIVISOR_MASK (255 << OUT_CLK_DIVISOR_SHIFT) +#define OUT_CLK_DIVISOR_MASK (0xffff << OUT_CLK_DIVISOR_SHIFT)
#define OUT_CLK_SOURCE_SHIFT 30
#define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT)
1.7.7.3
This looks reasonable from what I can see looking at the TRM for the CAR controller, so: Acked-by: Tom Warren twarren@nvidia.com
Tom

Add U-Boot's peripheral clock information to the Tegra20 device tree file.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Adjust definitions to fit new peripheral clock bindings - Change 'speed' to 'clock-frequency' - Remove u-boot,pinmux binding (sadly)
Changes in v3: - Move speed setting from tegra20.dtsi to board .dts file
arch/arm/dts/tegra20.dtsi | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/arch/arm/dts/tegra20.dtsi b/arch/arm/dts/tegra20.dtsi index df1eda4..e7536f7 100644 --- a/arch/arm/dts/tegra20.dtsi +++ b/arch/arm/dts/tegra20.dtsi @@ -45,6 +45,7 @@ compatible = "nvidia,tegra20-i2c"; reg = <0x7000C000 0x100>; interrupts = < 70 >; + clocks = <&periph_clk 12>; /* PERIPH_ID_I2C1 */ };
i2c@7000c400 { @@ -53,6 +54,7 @@ compatible = "nvidia,tegra20-i2c"; reg = <0x7000C400 0x100>; interrupts = < 116 >; + clocks = <&periph_clk 54>; /* PERIPH_ID_I2C2 */ };
i2c@7000c500 { @@ -61,14 +63,16 @@ compatible = "nvidia,tegra20-i2c"; reg = <0x7000C500 0x100>; interrupts = < 124 >; + clocks = <&periph_clk 67>; /* PERIPH_ID_I2C3 */ };
i2c@7000d000 { #address-cells = <1>; #size-cells = <0>; - compatible = "nvidia,tegra20-i2c"; + compatible = "nvidia,tegra20-i2c-dvc"; reg = <0x7000D000 0x200>; interrupts = < 85 >; + clocks = <&periph_clk 47>; /* PERIPH_ID_DVC_I2C */ };
i2s@70002800 {

Hello Simon,
Simon Glass wrote:
Add U-Boot's peripheral clock information to the Tegra20 device tree file.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
- Adjust definitions to fit new peripheral clock bindings
- Change 'speed' to 'clock-frequency'
- Remove u-boot,pinmux binding (sadly)
Changes in v3:
- Move speed setting from tegra20.dtsi to board .dts file
arch/arm/dts/tegra20.dtsi | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

From: Yen Lin yelin@nvidia.com
Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver requires CONFIG_OF_CONTROL to obtain its configuration from the device tree.
(Simon Glass: sjg@chromium.org modified for upstream)
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Adjust definitions to fit new peripheral clock bindings - Change i2c array to static - Make i2c/dvc decision come from fdt - Remove i2c configuring using CONFIG (use fdt instead) - Return an error if an unavailable i2c bus is selected - Simplify code in i2c_addr_ok() - Tidy comment style - Use DIV_ROUND_UP() instead of a home-grown macro - Use new fdtdec alias decode function
Changes in v3: - Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports - Add comment on how to select pinmux for I2C2 - Change i2c_init() to reset speed and override the fdt setting - Fix i2c_addr_ok() function to check addresses correctly - Rename driver to tegra_i2c since it will later be shared with Tegra3 - Tidy up I2C speed selection to use the peripheral clock
arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566 ++++++++++++++++++++++++++ include/fdtdec.h | 2 + lib/fdtdec.c | 2 + 5 files changed, 731 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c
diff --git a/arch/arm/include/asm/arch-tegra2/tegra_i2c.h b/arch/arm/include/asm/arch-tegra2/tegra_i2c.h new file mode 100644 index 0000000..86f6a01 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra_i2c.h @@ -0,0 +1,160 @@ +/* + * NVIDIA Tegra2 I2C controller + * + * Copyright 2010-2011 NVIDIA Corporation + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _TEGRA2_I2C_H_ +#define _TEGRA2_I2C_H_ + +#include <asm/types.h> + +/* Convert i2c slave address to be put on bus */ +#define I2C_ADDR_ON_BUS(chip) (chip << 1) + +enum { + I2C_TIMEOUT_USEC = 10000, /* Wait time for completion */ + I2C_FIFO_DEPTH = 8, /* I2C fifo depth */ +}; + +enum i2c_transaction_flags { + I2C_IS_WRITE = 0x1, /* for I2C write operation */ + I2C_IS_10_BIT_ADDRESS = 0x2, /* for 10-bit I2C slave address */ + I2C_USE_REPEATED_START = 0x4, /* for repeat start */ + I2C_NO_ACK = 0x8, /* for slave that won't generate ACK */ + I2C_SOFTWARE_CONTROLLER = 0x10, /* for I2C transfer using GPIO */ + I2C_NO_STOP = 0x20, +}; + +/* Contians the I2C transaction details */ +struct i2c_trans_info { + /* flags to indicate the transaction details */ + enum i2c_transaction_flags flags; + u32 address; /* I2C slave device address */ + u32 num_bytes; /* number of bytes to be transferred */ + /* + * Send/receive buffer. For the I2C send operation this buffer should + * be filled with the data to be sent to the slave device. For the I2C + * receive operation this buffer is filled with the data received from + * the slave device. + */ + u8 *buf; + int is_10bit_address; +}; + +struct i2c_control { + u32 tx_fifo; + u32 rx_fifo; + u32 packet_status; + u32 fifo_control; + u32 fifo_status; + u32 int_mask; + u32 int_status; +}; + +struct dvc_ctlr { + u32 ctrl1; /* 00: DVC_CTRL_REG1 */ + u32 ctrl2; /* 04: DVC_CTRL_REG2 */ + u32 ctrl3; /* 08: DVC_CTRL_REG3 */ + u32 status; /* 0C: DVC_STATUS_REG */ + u32 ctrl; /* 10: DVC_I2C_CTRL_REG */ + u32 addr_data; /* 14: DVC_I2C_ADDR_DATA_REG */ + u32 reserved_0[2]; /* 18: */ + u32 req; /* 20: DVC_REQ_REGISTER */ + u32 addr_data3; /* 24: DVC_I2C_ADDR_DATA_REG_3 */ + u32 reserved_1[6]; /* 28: */ + u32 cnfg; /* 40: DVC_I2C_CNFG */ + u32 cmd_addr0; /* 44: DVC_I2C_CMD_ADDR0 */ + u32 cmd_addr1; /* 48: DVC_I2C_CMD_ADDR1 */ + u32 cmd_data1; /* 4C: DVC_I2C_CMD_DATA1 */ + u32 cmd_data2; /* 50: DVC_I2C_CMD_DATA2 */ + u32 reserved_2[2]; /* 54: */ + u32 i2c_status; /* 5C: DVC_I2C_STATUS */ + struct i2c_control control; /* 60 ~ 78 */ +}; + +struct i2c_ctlr { + u32 cnfg; /* 00: I2C_I2C_CNFG */ + u32 cmd_addr0; /* 04: I2C_I2C_CMD_ADDR0 */ + u32 cmd_addr1; /* 08: I2C_I2C_CMD_DATA1 */ + u32 cmd_data1; /* 0C: I2C_I2C_CMD_DATA2 */ + u32 cmd_data2; /* 10: DVC_I2C_CMD_DATA2 */ + u32 reserved_0[2]; /* 14: */ + u32 status; /* 1C: I2C_I2C_STATUS */ + u32 sl_cnfg; /* 20: I2C_I2C_SL_CNFG */ + u32 sl_rcvd; /* 24: I2C_I2C_SL_RCVD */ + u32 sl_status; /* 28: I2C_I2C_SL_STATUS */ + u32 sl_addr1; /* 2C: I2C_I2C_SL_ADDR1 */ + u32 sl_addr2; /* 30: I2C_I2C_SL_ADDR2 */ + u32 reserved_1[2]; /* 34: */ + u32 sl_delay_count; /* 3C: I2C_I2C_SL_DELAY_COUNT */ + u32 reserved_2[4]; /* 40: */ + struct i2c_control control; /* 50 ~ 68 */ +}; + +/* bit fields definitions for IO Packet Header 1 format */ +#define PKT_HDR1_PROTOCOL_SHIFT 4 +#define PKT_HDR1_PROTOCOL_MASK (0xf << PKT_HDR1_PROTOCOL_SHIFT) +#define PKT_HDR1_CTLR_ID_SHIFT 12 +#define PKT_HDR1_CTLR_ID_MASK (0xf << PKT_HDR1_CTLR_ID_SHIFT) +#define PKT_HDR1_PKT_ID_SHIFT 16 +#define PKT_HDR1_PKT_ID_MASK (0xff << PKT_HDR1_PKT_ID_SHIFT) +#define PROTOCOL_TYPE_I2C 1 + +/* bit fields definitions for IO Packet Header 2 format */ +#define PKT_HDR2_PAYLOAD_SIZE_SHIFT 0 +#define PKT_HDR2_PAYLOAD_SIZE_MASK (0xfff << PKT_HDR2_PAYLOAD_SIZE_SHIFT) + +/* bit fields definitions for IO Packet Header 3 format */ +#define PKT_HDR3_READ_MODE_SHIFT 19 +#define PKT_HDR3_READ_MODE_MASK (1 << PKT_HDR3_READ_MODE_SHIFT) +#define PKT_HDR3_SLAVE_ADDR_SHIFT 0 +#define PKT_HDR3_SLAVE_ADDR_MASK (0x3ff << PKT_HDR3_SLAVE_ADDR_SHIFT) + +#define DVC_CTRL_REG3_I2C_HW_SW_PROG_SHIFT 26 +#define DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK \ + (1 << DVC_CTRL_REG3_I2C_HW_SW_PROG_SHIFT) + +/* I2C_CNFG */ +#define I2C_CNFG_NEW_MASTER_FSM_SHIFT 11 +#define I2C_CNFG_NEW_MASTER_FSM_MASK (1 << I2C_CNFG_NEW_MASTER_FSM_SHIFT) +#define I2C_CNFG_PACKET_MODE_SHIFT 10 +#define I2C_CNFG_PACKET_MODE_MASK (1 << I2C_CNFG_PACKET_MODE_SHIFT) + +/* I2C_SL_CNFG */ +#define I2C_SL_CNFG_NEWSL_SHIFT 2 +#define I2C_SL_CNFG_NEWSL_MASK (1 << I2C_SL_CNFG_NEWSL_SHIFT) + +/* I2C_FIFO_STATUS */ +#define TX_FIFO_FULL_CNT_SHIFT 0 +#define TX_FIFO_FULL_CNT_MASK (0xf << TX_FIFO_FULL_CNT_SHIFT) +#define TX_FIFO_EMPTY_CNT_SHIFT 4 +#define TX_FIFO_EMPTY_CNT_MASK (0xf << TX_FIFO_EMPTY_CNT_SHIFT) + +/* I2C_INTERRUPT_STATUS */ +#define I2C_INT_XFER_COMPLETE_SHIFT 7 +#define I2C_INT_XFER_COMPLETE_MASK (1 << I2C_INT_XFER_COMPLETE_SHIFT) +#define I2C_INT_NO_ACK_SHIFT 3 +#define I2C_INT_NO_ACK_MASK (1 << I2C_INT_NO_ACK_SHIFT) +#define I2C_INT_ARBITRATION_LOST_SHIFT 2 +#define I2C_INT_ARBITRATION_LOST_MASK (1 << I2C_INT_ARBITRATION_LOST_SHIFT) + +#endif diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 504db03..f86e46c 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -41,6 +41,7 @@ COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o +COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c new file mode 100644 index 0000000..1a4855a --- /dev/null +++ b/drivers/i2c/tegra_i2c.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Copyright (c) 2010-2011 NVIDIA Corporation + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/tegra_i2c.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +static unsigned int i2c_bus_num; + +/* Information about i2c controller */ +struct i2c_bus { + int id; + enum periph_id periph_id; + int speed; + int pinmux_config; + struct i2c_control *control; + struct i2c_ctlr *regs; + int is_dvc; /* DVC type, rather than I2C */ + int inited; /* bus is inited */ +}; + +static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS]; + +static void set_packet_mode(struct i2c_bus *i2c_bus) +{ + u32 config; + + config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK; + + if (i2c_bus->is_dvc) { + struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; + + writel(config, &dvc->cnfg); + } else { + writel(config, &i2c_bus->regs->cnfg); + /* + * program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe + * issues, i.e., some slaves may be wrongly detected. + */ + setbits_le32(&i2c_bus->regs->sl_cnfg, I2C_SL_CNFG_NEWSL_MASK); + } +} + +static void i2c_reset_controller(struct i2c_bus *i2c_bus) +{ + /* Reset I2C controller. */ + reset_periph(i2c_bus->periph_id, 1); + + /* re-program config register to packet mode */ + set_packet_mode(i2c_bus); +} + +static void i2c_init_controller(struct i2c_bus *i2c_bus) +{ + /* + * Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8 + * here, in section 23.3.1, but in fact we seem to need a factor of + * 16 to get the right frequency. + */ + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, + i2c_bus->speed * 2 * 8); + + /* Reset I2C controller. */ + i2c_reset_controller(i2c_bus); + + /* Configure I2C controller. */ + if (i2c_bus->is_dvc) { /* only for DVC I2C */ + struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; + + setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); + } + + funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config); +} + +static void send_packet_headers( + struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans, + u32 packet_id) +{ + u32 data; + + /* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */ + data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT; + data |= packet_id << PKT_HDR1_PKT_ID_SHIFT; + data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 1 sent (0x%x)\n", data); + + /* prepare header2 */ + data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 2 sent (0x%x)\n", data); + + /* prepare IO specific header: configure the slave address */ + data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT; + + /* Enable Read if it is not a write transaction */ + if (!(trans->flags & I2C_IS_WRITE)) + data |= PKT_HDR3_READ_MODE_MASK; + + /* Write I2C specific header */ + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 3 sent (0x%x)\n", data); +} + +static int wait_for_tx_fifo_empty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_EMPTY_CNT_MASK) + >> TX_FIFO_EMPTY_CNT_SHIFT; + if (count == I2C_FIFO_DEPTH) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_rx_fifo_notempty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_FULL_CNT_MASK) + >> TX_FIFO_FULL_CNT_SHIFT; + if (count) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_transfer_complete(struct i2c_control *control) +{ + int int_status; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + int_status = readl(&control->int_status); + if (int_status & I2C_INT_NO_ACK_MASK) + return -int_status; + if (int_status & I2C_INT_ARBITRATION_LOST_MASK) + return -int_status; + if (int_status & I2C_INT_XFER_COMPLETE_MASK) + return 0; + + udelay(10); + timeout_us -= 10; + } + + return -1; +} + +static int send_recv_packets( + struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans) +{ + struct i2c_control *control = i2c_bus->control; + u32 int_status; + u32 words; + u8 *dptr; + u32 local; + uchar last_bytes; + int error = 0; + int is_write = trans->flags & I2C_IS_WRITE; + + /* clear status from previous transaction, XFER_COMPLETE, NOACK, etc. */ + int_status = readl(&control->int_status); + writel(int_status, &control->int_status); + + send_packet_headers(i2c_bus, trans, 1); + + words = DIV_ROUND_UP(trans->num_bytes, 4); + last_bytes = trans->num_bytes & 3; + dptr = trans->buf; + + while (words) { + if (is_write) { + /* deal with word alignment */ + if ((unsigned)dptr & 3) { + memcpy(&local, dptr, sizeof(u32)); + writel(local, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", local); + } else { + writel(*(u32 *)dptr, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", *(u32 *)dptr); + } + if (!wait_for_tx_fifo_empty(control)) { + error = -1; + goto exit; + } + } else { + if (!wait_for_rx_fifo_notempty(control)) { + error = -1; + goto exit; + } + /* + * for the last word, we read into our local buffer, + * in case that caller did not provide enough buffer. + */ + local = readl(&control->rx_fifo); + if ((words == 1) && last_bytes) + memcpy(dptr, (char *)&local, last_bytes); + else if ((unsigned)dptr & 3) + memcpy(dptr, &local, sizeof(u32)); + else + *(u32 *)dptr = local; + debug("pkt data received (0x%x)\n", local); + } + words--; + dptr += sizeof(u32); + } + + if (wait_for_transfer_complete(control)) { + error = -1; + goto exit; + } + return 0; +exit: + /* error, reset the controller. */ + i2c_reset_controller(i2c_bus); + + return error; +} + +static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr; + trans_info.buf = data; + trans_info.flags = I2C_IS_WRITE; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info); + if (error) + debug("tegra2_i2c_write_data: Error (%d) !!!\n", error); + + return error; +} + +static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr | 1; + trans_info.buf = data; + trans_info.flags = 0; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info); + if (error) + debug("tegra2_i2c_read_data: Error (%d) !!!\n", error); + + return error; +} + +#ifndef CONFIG_OF_CONTROL +#error "Please enable device tree support to use this driver" +#endif + +unsigned int i2c_get_bus_speed(void) +{ + return i2c_controllers[i2c_bus_num].speed; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + struct i2c_bus *i2c_bus; + + i2c_bus = &i2c_controllers[i2c_bus_num]; + i2c_bus->speed = speed; + i2c_init_controller(i2c_bus); + + return 0; +} + +static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) +{ + i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); + + /* + * We don't have a binding for pinmux yet. Leave it out for now. So + * far no one needs anything other than the default. + */ + i2c_bus->pinmux_config = FUNCMUX_DEFAULT; + i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0); + i2c_bus->periph_id = fdtdec_decode_periph_id(blob, node); + + /* + * We can't specify the pinmux config in the fdt, so I2C2 will not + * work on Seaboard. It normally has no devices on it anyway. + * You could add in this little hack if you need to use it. + * The correct solution is a pinmux binding in the fdt. + * + * if (i2c_bus->periph_id == PERIPH_ID_I2C2) + * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; + */ + if (i2c_bus->periph_id == -1) + return -FDT_ERR_NOTFOUND; + + return 0; +} + +/* + * Process a list of nodes, adding them to our list of I2C ports. + * + * @param blob fdt blob + * @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 + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count, + int is_dvc) +{ + struct i2c_bus *i2c_bus; + int i; + + /* build the i2c_controllers[] for each controller */ + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; + + i2c_bus = &i2c_controllers[i]; + i2c_bus->id = i; + + if (i2c_get_config(blob, node, i2c_bus)) { + printf("i2c_init_board: failed to decode bus %d\n", i); + return -1; + } + + i2c_bus->is_dvc = is_dvc; + if (is_dvc) { + i2c_bus->control = + &((struct dvc_ctlr *)i2c_bus->regs)->control; + } else { + i2c_bus->control = &i2c_bus->regs->control; + } + debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", + is_dvc ? "dvc" : "i2c", i, i2c_bus->regs, + i2c_bus->periph_id, i2c_bus->speed); + i2c_init_controller(i2c_bus); + debug("ok\n"); + i2c_bus->inited = 1; + + /* Mark position as used */ + node_list[i] = -1; + } + + return 0; +} + +int i2c_init_board(void) +{ + int node_list[TEGRA_I2C_NUM_CONTROLLERS]; + const void *blob = gd->fdt_blob; + int count; + + /* First get the 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)) + return -1; + + /* 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)) + return -1; + + return 0; +} + +void i2c_init(int speed, int slaveaddr) +{ + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + i2c_set_bus_speed(speed); +} + +/* i2c write version without the register address */ +int i2c_write_data(uchar chip, uchar *buffer, int len) +{ + int rc; + + debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len); + debug("write_data: "); + /* use rc for counter */ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + rc = tegra2_i2c_write_data(I2C_ADDR_ON_BUS(chip), buffer, len); + if (rc) + debug("i2c_write_data(): rc=%d\n", rc); + + return rc; +} + +/* i2c read version without the register address */ +int i2c_read_data(uchar chip, uchar *buffer, int len) +{ + int rc; + + debug("inside i2c_read_data():\n"); + rc = tegra2_i2c_read_data(I2C_ADDR_ON_BUS(chip), buffer, len); + if (rc) { + debug("i2c_read_data(): rc=%d\n", rc); + return rc; + } + + debug("i2c_read_data: "); + /* reuse rc for counter*/ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + return 0; +} + +/* Probe to see if a chip is present. */ +int i2c_probe(uchar chip) +{ + int rc; + uchar reg; + + debug("i2c_probe: addr=0x%x\n", chip); + reg = 0; + rc = i2c_write_data(chip, ®, 1); + if (rc) { + debug("Error probing 0x%x.\n", chip); + return 1; + } + return 0; +} + +static int i2c_addr_ok(const uint addr, const int alen) +{ + /* We support 7 or 10 bit addresses, so one or two bytes each */ + return alen == 1 || alen == 2; +} + +/* Read bytes */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + uint offset; + int i; + + debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, len); + if (!i2c_addr_ok(addr, alen)) { + debug("i2c_read: Bad address %x.%d.\n", addr, alen); + return 1; + } + for (offset = 0; offset < len; offset++) { + if (alen) { + uchar data[alen]; + for (i = 0; i < alen; i++) { + data[alen - i - 1] = + (addr + offset) >> (8 * i); + } + if (i2c_write_data(chip, data, alen)) { + debug("i2c_read: error sending (0x%x)\n", + addr); + return 1; + } + } + if (i2c_read_data(chip, buffer + offset, 1)) { + debug("i2c_read: error reading (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +/* Write bytes */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + uint offset; + int i; + + debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, len); + if (!i2c_addr_ok(addr, alen)) { + debug("i2c_write: Bad address %x.%d.\n", addr, alen); + return 1; + } + for (offset = 0; offset < len; offset++) { + uchar data[alen + 1]; + for (i = 0; i < alen; i++) + data[alen - i - 1] = (addr + offset) >> (8 * i); + data[alen] = buffer[offset]; + if (i2c_write_data(chip, data, alen + 1)) { + debug("i2c_write: error sending (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Functions for multiple I2C bus handling + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +int i2c_set_bus_num(unsigned int bus) +{ + if (bus >= TEGRA_I2C_NUM_CONTROLLERS || !i2c_controllers[bus].inited) + return -1; + i2c_bus_num = bus; + + return 0; +} +#endif diff --git a/include/fdtdec.h b/include/fdtdec.h index b028bfb..2ab1c68 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -58,6 +58,8 @@ struct fdt_memory { enum fdt_compat_id { COMPAT_UNKNOWN, COMPAT_NVIDIA_TEGRA20_USB, /* Tegra2 USB port */ + COMPAT_NVIDIA_TEGRA20_I2C, /* Tegra2 i2c */ + COMPAT_NVIDIA_TEGRA20_DVC, /* Tegra2 dvc (really just i2c) */
COMPAT_COUNT, }; diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 35361d1..8187bd1 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -38,6 +38,8 @@ DECLARE_GLOBAL_DATA_PTR; static const char * const compat_names[COMPAT_COUNT] = { COMPAT(UNKNOWN, "<none>"), COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"), + COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"), + COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"), };
const char *fdtdec_get_compatible(enum fdt_compat_id id)

On Fri, 2012-02-03 at 17:13 -0800, Simon Glass wrote:
From: Yen Lin yelin@nvidia.com
Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver requires CONFIG_OF_CONTROL to obtain its configuration from the device tree.
(Simon Glass: sjg@chromium.org modified for upstream)
Signed-off-by: Simon Glass sjg@chromium.org
Signed-off-by: Yen Lin yelin@nvidia.com

Hello Simon,
Simon Glass wrote:
From: Yen Lin yelin@nvidia.com
Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver requires CONFIG_OF_CONTROL to obtain its configuration from the device tree.
(Simon Glass: sjg@chromium.org modified for upstream)
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
- Adjust definitions to fit new peripheral clock bindings
- Change i2c array to static
- Make i2c/dvc decision come from fdt
- Remove i2c configuring using CONFIG (use fdt instead)
- Return an error if an unavailable i2c bus is selected
- Simplify code in i2c_addr_ok()
- Tidy comment style
- Use DIV_ROUND_UP() instead of a home-grown macro
- Use new fdtdec alias decode function
Changes in v3:
- Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports
- Add comment on how to select pinmux for I2C2
- Change i2c_init() to reset speed and override the fdt setting
- Fix i2c_addr_ok() function to check addresses correctly
- Rename driver to tegra_i2c since it will later be shared with Tegra3
- Tidy up I2C speed selection to use the peripheral clock
arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566 ++++++++++++++++++++++++++ include/fdtdec.h | 2 + lib/fdtdec.c | 2 + 5 files changed, 731 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

This enables I2C on all Nvidia boards including Seaboard and Harmony.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined
Changes in v3: - Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
board/nvidia/common/board.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 72d8630..85dd359 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -34,6 +34,7 @@ #include <asm/arch/uart.h> #include <spi.h> #include <asm/arch/usb.h> +#include <i2c.h> #include "board.h"
DECLARE_GLOBAL_DATA_PTR; @@ -75,6 +76,12 @@ int board_init(void) #endif /* boot param addr */ gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100); +#ifdef CONFIG_TEGRA_I2C +#ifndef CONFIG_SYS_I2C_INIT_BOARD +#error "You must define CONFIG_SYS_I2C_INIT_BOARD to use i2c on Nvidia boards" +#endif + i2c_init_board(); +#endif
#ifdef CONFIG_USB_EHCI_TEGRA pin_mux_usb();

Hello Simon,
Simon Glass wrote:
This enables I2C on all Nvidia boards including Seaboard and Harmony.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
- Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined
Changes in v3:
- Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
board/nvidia/common/board.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

Select the port ordering for I2C on Seaboard.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Disable port 2 as it is not used
Changes in v3: - Move speed setting from tegra20.dtsi to board .dts file
board/nvidia/dts/tegra2-seaboard.dts | 21 +++++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/board/nvidia/dts/tegra2-seaboard.dts b/board/nvidia/dts/tegra2-seaboard.dts index f00341f..95fb561 100644 --- a/board/nvidia/dts/tegra2-seaboard.dts +++ b/board/nvidia/dts/tegra2-seaboard.dts @@ -15,6 +15,11 @@ /* This defines the order of our USB ports */ usb0 = "/usb@c5008000"; usb1 = "/usb@c5000000"; + + i2c0 = "/i2c@7000d000"; + i2c1 = "/i2c@7000c000"; + i2c2 = "/i2c@7000c400"; + i2c3 = "/i2c@7000c500"; };
memory { @@ -44,4 +49,20 @@ usb@c5004000 { status = "disabled"; }; + + i2c@7000c000 { + clock-frequency = <100000>; + }; + + i2c@7000c400 { + status = "disabled"; + }; + + i2c@7000c500 { + clock-frequency = <100000>; + }; + + i2c@7000d000 { + clock-frequency = <100000>; + }; };

Hello Simon,
Simon Glass wrote:
Select the port ordering for I2C on Seaboard.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
- Disable port 2 as it is not used
Changes in v3:
- Move speed setting from tegra20.dtsi to board .dts file
board/nvidia/dts/tegra2-seaboard.dts | 21 +++++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-)
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

The Tegra 2x SOC has four ports, so define TEGRA_I2C_NUM_CONTROLLERS in the shared config file.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/configs/tegra2-common.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2-common.h index 266d0e5..837f859 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -94,6 +94,9 @@ #define CONFIG_EHCI_IS_TDI #define CONFIG_EHCI_DCACHE
+/* Total I2C ports on Tegra2 */ +#define TEGRA_I2C_NUM_CONTROLLERS 4 + /* include default commands */ #include <config_cmd_default.h>

Hello Simon,
Simon Glass wrote:
The Tegra 2x SOC has four ports, so define TEGRA_I2C_NUM_CONTROLLERS in the shared config file.
Signed-off-by: Simon Glass sjg@chromium.org
include/configs/tegra2-common.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

This enables I2C on Seaboard.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v3: - Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
include/configs/seaboard.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/configs/seaboard.h b/include/configs/seaboard.h index 6937bcc..ea7e4f0 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -64,6 +64,14 @@ #define CONFIG_CMD_SF #define CONFIG_SPI_FLASH_SIZE (4 << 20)
+/* I2C */ +#define CONFIG_TEGRA_I2C +#define CONFIG_SYS_I2C_INIT_BOARD +#define CONFIG_I2C_MULTI_BUS +#define CONFIG_SYS_MAX_I2C_BUS 4 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_CMD_I2C + /* SD/MMC */ #define CONFIG_MMC #define CONFIG_GENERIC_MMC

Hello Simon,
Simon Glass wrote:
This enables I2C on Seaboard.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
include/configs/seaboard.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

Hello Tom,
Simon Glass wrote:
This series brings in an I2C driver for Tegra which can be configured by a flat device tree.
It supports 8- and 16-bit addresses and both the normal I2C ports and the DVC port (for controlling the power management unit (PMU)).
Recent Linux bindings are used, based on example .dts files found in branch for-3.3/dt at:
git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra.git
(I could not find the actual binding documentation to include here)
Note: There is still an open question on I2C2 pinmux. The idea of an 'nvidia,pinmux-config' settings did not find favour, so we have no way of passing the correct config to funcmux for this port. Therefore we have disabled it for now. The impact on Seaboard is that the TPM and battery charger cannot be accessed.
I am sending this series to the linux-tegra list also, and will do so for future series also. This is because anything to do with fdt also affects the kernel bindings. Please advise if this is not useful.
Changes in v2:
- Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined
- Adjust definitions to fit new peripheral clock bindings
- Change 'speed' to 'clock-frequency'
- Change i2c array to static
- Disable port 2 as it is not used
- Make i2c/dvc decision come from fdt
- Remove i2c configuring using CONFIG (use fdt instead)
- Remove u-boot,pinmux binding (sadly)
- Return an error if an unavailable i2c bus is selected
- Simplify code in i2c_addr_ok()
- Tidy comment style
- Use DIV_ROUND_UP() instead of a home-grown macro
- Use new fdtdec alias decode function
Changes in v3:
- Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports
- Add comment on how to select pinmux for I2C2
- Add comments and warning for mixed alias use in fdtdec
- Add new patch to support 16-bit clock divisors required by I2C
- Change i2c_init() to reset speed and override the fdt setting
- Fix i2c_addr_ok() function to check addresses correctly
- Move speed setting from tegra20.dtsi to board .dts file
- Rename driver to tegra_i2c since it will later be shared with Tegra3
- Tidy up I2C speed selection to use the peripheral clock
- Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
Simon Glass (8): tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE fdt: Add function to allow aliases to refer to multiple nodes tegra: Enhance clock support to handle 16-bit clock divisors tegra: fdt: i2c: Add extra I2C bindings for U-Boot tegra: i2c: Initialise I2C on Nvidia boards tegra: i2c: Select I2C ordering for Seaboard tegra: i2c: Select number of controllers for Tegra2 boards tegra: i2c: Enable I2C on Seaboard
Yen Lin (1): tegra: i2c: Add I2C driver
arch/arm/cpu/armv7/tegra2/ap20.c | 10 +- arch/arm/cpu/armv7/tegra2/board.c | 2 +- arch/arm/cpu/armv7/tegra2/clock.c | 63 ++- arch/arm/dts/tegra20.dtsi | 6 +- arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 +- arch/arm/include/asm/arch-tegra2/tegra2.h | 4 +- arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ board/nvidia/common/board.c | 7 + board/nvidia/dts/tegra2-seaboard.dts | 21 + drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566 ++++++++++++++++++++++++++ include/configs/seaboard.h | 8 + include/configs/tegra2-common.h | 3 + include/fdtdec.h | 25 ++ lib/fdtdec.c | 24 +- 15 files changed, 873 insertions(+), 37 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c
Do you want to pick up this patchserie? So I can send my Ack for it. If I should pick it up, please send your Acked-by, thanks!
bye, Heiko

Heiko,
-----Original Message----- From: Heiko Schocher [mailto:hs@denx.de] Sent: Thursday, February 09, 2012 12:02 AM To: Simon Glass Cc: U-Boot Mailing List; linux-tegra@vger.kernel.org; Tom Warren Subject: Re: [U-Boot] [PATCH v3 0/9] tegra: Add I2C driver and associated parts
Hello Tom,
Simon Glass wrote:
This series brings in an I2C driver for Tegra which can be configured by a flat device tree.
It supports 8- and 16-bit addresses and both the normal I2C ports and the DVC port (for controlling the power management unit (PMU)).
Recent Linux bindings are used, based on example .dts files found in branch for-3.3/dt at:
git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra.git
(I could not find the actual binding documentation to include here)
Note: There is still an open question on I2C2 pinmux. The idea of an 'nvidia,pinmux-config' settings did not find favour, so we have no way of passing the correct config to funcmux for this port. Therefore we have disabled it for now. The impact on Seaboard is that the TPM and battery charger cannot be accessed.
I am sending this series to the linux-tegra list also, and will do so for future series also. This is because anything to do with fdt also affects the kernel bindings. Please advise if this is not useful.
Changes in v2:
- Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined
- Adjust definitions to fit new peripheral clock bindings
- Change 'speed' to 'clock-frequency'
- Change i2c array to static
- Disable port 2 as it is not used
- Make i2c/dvc decision come from fdt
- Remove i2c configuring using CONFIG (use fdt instead)
- Remove u-boot,pinmux binding (sadly)
- Return an error if an unavailable i2c bus is selected
- Simplify code in i2c_addr_ok()
- Tidy comment style
- Use DIV_ROUND_UP() instead of a home-grown macro
- Use new fdtdec alias decode function
Changes in v3:
- Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports
- Add comment on how to select pinmux for I2C2
- Add comments and warning for mixed alias use in fdtdec
- Add new patch to support 16-bit clock divisors required by I2C
- Change i2c_init() to reset speed and override the fdt setting
- Fix i2c_addr_ok() function to check addresses correctly
- Move speed setting from tegra20.dtsi to board .dts file
- Rename driver to tegra_i2c since it will later be shared with Tegra3
- Tidy up I2C speed selection to use the peripheral clock
- Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
Simon Glass (8): tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE fdt: Add function to allow aliases to refer to multiple nodes tegra: Enhance clock support to handle 16-bit clock divisors tegra: fdt: i2c: Add extra I2C bindings for U-Boot tegra: i2c: Initialise I2C on Nvidia boards tegra: i2c: Select I2C ordering for Seaboard tegra: i2c: Select number of controllers for Tegra2 boards tegra: i2c: Enable I2C on Seaboard
Yen Lin (1): tegra: i2c: Add I2C driver
arch/arm/cpu/armv7/tegra2/ap20.c | 10 +- arch/arm/cpu/armv7/tegra2/board.c | 2 +- arch/arm/cpu/armv7/tegra2/clock.c | 63 ++- arch/arm/dts/tegra20.dtsi | 6 +- arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 +- arch/arm/include/asm/arch-tegra2/tegra2.h | 4 +- arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ board/nvidia/common/board.c | 7 + board/nvidia/dts/tegra2-seaboard.dts | 21 + drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566
++++++++++++++++++++++++++
include/configs/seaboard.h | 8 + include/configs/tegra2-common.h | 3 + include/fdtdec.h | 25 ++ lib/fdtdec.c | 24 +- 15 files changed, 873 insertions(+), 37 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c
Do you want to pick up this patchserie? So I can send my Ack for it. If I should pick it up, please send your Acked-by, thanks!
I'm not sure what you mean by 'pick up' this patch series. I'm applying these (USB, then I2C) to u-boot-tegra as they are Acked in toto. USB is still pending a single (#10 of 18) Ack by Stephen Warren for the CAR binding rewrite. I2C depends on the fdt stuff in the USB series, so it's blocked until USB is resolved.
I've put the latest USB patchset (v5/v6) in a 'test' branch in u-boot-tegra - I'll push that to denx.de. I can also add the I2C patchset (assuming it applies w/o error) and push that, too, if it would help anyone. But I can't in good conscience apply it to u-boot-tegra/master or /next until it's fully Acked. And I don't have enough fdt, USB or I2C knowledge to do the Acking myself. If you are offering to Ack the entire I2C series, that's great, and will speed up getting it into u-boot-tegra once the USB issue is resolved (hopefully next week).
Thanks,
Tom

Hello tom,
Tom Warren wrote:
Heiko,
-----Original Message----- From: Heiko Schocher [mailto:hs@denx.de] Sent: Thursday, February 09, 2012 12:02 AM To: Simon Glass Cc: U-Boot Mailing List; linux-tegra@vger.kernel.org; Tom Warren Subject: Re: [U-Boot] [PATCH v3 0/9] tegra: Add I2C driver and associated parts
Hello Tom,
Simon Glass wrote:
This series brings in an I2C driver for Tegra which can be configured by a flat device tree.
It supports 8- and 16-bit addresses and both the normal I2C ports and the DVC port (for controlling the power management unit (PMU)).
Recent Linux bindings are used, based on example .dts files found in branch for-3.3/dt at:
git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra.git
(I could not find the actual binding documentation to include here)
Note: There is still an open question on I2C2 pinmux. The idea of an 'nvidia,pinmux-config' settings did not find favour, so we have no way of passing the correct config to funcmux for this port. Therefore we have disabled it for now. The impact on Seaboard is that the TPM and battery charger cannot be accessed.
I am sending this series to the linux-tegra list also, and will do so for future series also. This is because anything to do with fdt also affects the kernel bindings. Please advise if this is not useful.
Changes in v2:
- Add build warning if CONFIG_SYS_I2C_INIT_BOARD is not defined
- Adjust definitions to fit new peripheral clock bindings
- Change 'speed' to 'clock-frequency'
- Change i2c array to static
- Disable port 2 as it is not used
- Make i2c/dvc decision come from fdt
- Remove i2c configuring using CONFIG (use fdt instead)
- Remove u-boot,pinmux binding (sadly)
- Return an error if an unavailable i2c bus is selected
- Simplify code in i2c_addr_ok()
- Tidy comment style
- Use DIV_ROUND_UP() instead of a home-grown macro
- Use new fdtdec alias decode function
Changes in v3:
- Add TEGRA_I2C_NUM_CONTROLLERS to select number of I2C ports
- Add comment on how to select pinmux for I2C2
- Add comments and warning for mixed alias use in fdtdec
- Add new patch to support 16-bit clock divisors required by I2C
- Change i2c_init() to reset speed and override the fdt setting
- Fix i2c_addr_ok() function to check addresses correctly
- Move speed setting from tegra20.dtsi to board .dts file
- Rename driver to tegra_i2c since it will later be shared with Tegra3
- Tidy up I2C speed selection to use the peripheral clock
- Use CONFIG_TEGRA_I2C instead of CONFIG_TEGRA2_I2C
Simon Glass (8): tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE fdt: Add function to allow aliases to refer to multiple nodes tegra: Enhance clock support to handle 16-bit clock divisors tegra: fdt: i2c: Add extra I2C bindings for U-Boot tegra: i2c: Initialise I2C on Nvidia boards tegra: i2c: Select I2C ordering for Seaboard tegra: i2c: Select number of controllers for Tegra2 boards tegra: i2c: Enable I2C on Seaboard
Yen Lin (1): tegra: i2c: Add I2C driver
arch/arm/cpu/armv7/tegra2/ap20.c | 10 +- arch/arm/cpu/armv7/tegra2/board.c | 2 +- arch/arm/cpu/armv7/tegra2/clock.c | 63 ++- arch/arm/dts/tegra20.dtsi | 6 +- arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 +- arch/arm/include/asm/arch-tegra2/tegra2.h | 4 +- arch/arm/include/asm/arch-tegra2/tegra_i2c.h | 160 ++++++++ board/nvidia/common/board.c | 7 + board/nvidia/dts/tegra2-seaboard.dts | 21 + drivers/i2c/Makefile | 1 + drivers/i2c/tegra_i2c.c | 566
++++++++++++++++++++++++++
include/configs/seaboard.h | 8 + include/configs/tegra2-common.h | 3 + include/fdtdec.h | 25 ++ lib/fdtdec.c | 24 +- 15 files changed, 873 insertions(+), 37 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra_i2c.h create mode 100644 drivers/i2c/tegra_i2c.c
Do you want to pick up this patchserie? So I can send my Ack for it. If I should pick it up, please send your Acked-by, thanks!
I'm not sure what you mean by 'pick up' this patch series. I'm applying these (USB, then I2C) to u-boot-tegra as they are Acked in toto. USB is still pending a single (#10 of 18) Ack by Stephen Warren for the CAR binding rewrite. I2C depends on the fdt stuff in the USB series, so it's blocked until USB is resolved.
Ok, thanks for the explanation!
I've put the latest USB patchset (v5/v6) in a 'test' branch in u-boot-tegra - I'll push that to denx.de. I can also add the I2C patchset (assuming it applies w/o error) and push that, too, if it would help anyone. But I can't in good conscience apply it to u-boot-tegra/master or /next until it's fully Acked. And I don't have enough fdt, USB or I2C knowledge to do the Acking myself. If you are offering to Ack the entire I2C series, that's great, and will speed up getting it into u-boot-tegra once the USB issue is resolved (hopefully next week).
Ok, so I ack them.
bye, Heiko
participants (4)
-
Heiko Schocher
-
Simon Glass
-
Tom Warren
-
Yen Lin