Skip to contentSkip to conversation

tegra: Add I2C driver and associated parts

PATCH Superseded Simon Glass 5 participants 37 messages Dec 26, 2011 – Feb 3, 2012 2 messages not in the archive

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover 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)).

Simon Glass (6):
tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE
tegra: fdt: Add extra I2C definitions for U-Boot
tegra: Add I2C support to funcmux
tegra: Initialise I2C on Nvidia boards
tegra: Select I2C ordering for Seaboard
tegra: Enable I2C on Seaboard

Yen Lin (1):
tegra: Add I2C driver

 arch/arm/cpu/armv7/tegra2/ap20.c              |   10 +-
 arch/arm/cpu/armv7/tegra2/board.c             |    2 +-
 arch/arm/cpu/armv7/tegra2/funcmux.c           |   75 +++-
 arch/arm/dts/tegra20.dtsi                     |   12 +
 arch/arm/include/asm/arch-tegra2/funcmux.h    |    3 +
 arch/arm/include/asm/arch-tegra2/tegra2.h     |    8 +-
 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h |  167 ++++++++
 board/nvidia/common/board.c                   |    4 +
 board/nvidia/dts/tegra2-seaboard.dts          |    5 +
 drivers/i2c/Makefile                          |    1 +
 drivers/i2c/tegra2_i2c.c                      |  533 +++++++++++++++++++++++++
 include/configs/seaboard.h                    |    8 +
 include/fdtdec.h                              |    1 +
 lib/fdtdec.c                                  |    1 +
 14 files changed, 809 insertions(+), 21 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
 create mode 100644 drivers/i2c/tegra2_i2c.c
Signature · 1 line
1.7.3.1
#1 lore raw

PATCH 1/7 tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE

Change this name to fit with the current convention in the Tegra header file.

---
 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(-)
arch/arm/cpu/armv7/tegra2/ap20.c +5 -5
index 4c44bb3..3ea2e26 100644
@@ -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 */
arch/arm/cpu/armv7/tegra2/board.c +1 -1
index ea06570..2c91e69 100644
@@ -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);
arch/arm/include/asm/arch-tegra2/tegra2.h +2 -2
index baae2eb..ca1881e 100644
@@ -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 */
Signature · 1 line
1.7.3.1
#2 lore raw

PATCH 1/7 tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE

Quoted 85 lines

Change this name to fit with the current convention in the Tegra header file.

---
 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(-)
(quoted diff) +0 -0

b/arch/arm/cpu/armv7/tegra2/ap20.c index 4c44bb3..3ea2e26 100644

arch/arm/cpu/armv7/tegra2/ap20.c +5 -5
@@ -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 */
(quoted diff) +0 -0

b/arch/arm/cpu/armv7/tegra2/board.c index ea06570..2c91e69 100644

arch/arm/cpu/armv7/tegra2/board.c +1 -1
@@ -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);
(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2.h index baae2eb..ca1881e 100644

arch/arm/include/asm/arch-tegra2/tegra2.h +1 -1
@@ -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

arch/arm/include/asm/arch-tegra2/tegra2.h struct timerus { · line 61

Quoted 8 lines
arch/arm/include/asm/arch-tegra2/tegra2.h +1 -1
@@ -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 */

Looks ok

#4 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

In reply to Simon Glass

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

---
 arch/arm/dts/tegra20.dtsi |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)
arch/arm/dts/tegra20.dtsi +12 -0
index 27e3127..41ee0b2 100644
@@ -18,6 +18,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C000 0x100>;
 		interrupts = < 70 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <12>;	// PERIPH_ID_I2C1
 	};
 
 	i2c at 7000c400 {
@@ -26,6 +29,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C400 0x100>;
 		interrupts = < 116 >;
+		u-boot,pinmux = <1>;
+		speed = <100000>;
+		u-boot,periph-id = <54>;	// PERIPH_ID_I2C2
 	};
 
 	i2c at 7000c500 {
@@ -34,6 +40,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C500 0x100>;
 		interrupts = < 124 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <67>;	// PERIPH_ID_I2C3
 	};
 
 	i2c at 7000d000 {
@@ -42,6 +51,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000D000 0x200>;
 		interrupts = < 85 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <47>;	// PERIPH_ID_DVC_I2C
 	};
 
 	i2s at 70002800 {
Signature · 1 line
1.7.3.1
#5 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

Quoted 2 lines

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

Isn't this supposed to go to Linux kernel ?

M

Quoted 50 lines
---
 arch/arm/dts/tegra20.dtsi |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)
arch/arm/dts/tegra20.dtsi +12 -0
index 27e3127..41ee0b2 100644
@@ -18,6 +18,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C000 0x100>;
 		interrupts = < 70 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <12>;	// PERIPH_ID_I2C1
 	};
 
 	i2c at 7000c400 {
@@ -26,6 +29,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C400 0x100>;
 		interrupts = < 116 >;
+		u-boot,pinmux = <1>;
+		speed = <100000>;
+		u-boot,periph-id = <54>;	// PERIPH_ID_I2C2
 	};
 
 	i2c at 7000c500 {
@@ -34,6 +40,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C500 0x100>;
 		interrupts = < 124 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <67>;	// PERIPH_ID_I2C3
 	};
 
 	i2c at 7000d000 {
@@ -42,6 +51,9 @@
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000D000 0x200>;
 		interrupts = < 85 >;
+		u-boot,pinmux = <0>;
+		speed = <100000>;
+		u-boot,periph-id = <47>;	// PERIPH_ID_DVC_I2C
 	};
 
 	i2s at 70002800 {
#6 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

In reply to Simon Glass

Simon Glass wrote at Monday, December 26, 2011 11:12 AM:

Quoted 4 lines

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

arch/arm/dts/tegra20.dtsi +0 -0

...

arch/arm/dts/tegra20.dtsi line 21

Quoted 4 lines
arch/arm/dts/tegra20.dtsi +1 -0
 		compatible = "nvidia,tegra20-i2c";
 		reg = <0x7000C000 0x100>;
 		interrupts = < 70 >;
+		u-boot,pinmux = <0>;

That isn't acceptable to me for the same reasons I've outline many times before while discussing USB.

At least periph-id can be argued to be related to the HW, but the pinmux value is a hack that really has no place in device tree.

arch/arm/dts/tegra20.dtsi

Quoted 1 line
arch/arm/dts/tegra20.dtsi +1 -0
+		speed = <100000>;

That's already defined as "clock-frequency" in the Linux kernel.

Quoted 1 line
arch/arm/dts/tegra20.dtsi +1 -0
+		u-boot,periph-id = <12>;	// PERIPH_ID_I2C1
Signature · 1 line
nvpublic
#7 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

Hi Stephen,

Quoted 11 lines · Stephen Warren

Simon Glass wrote at Monday, December 26, 2011 11:12 AM:

Quoted 4 lines

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

arch/arm/dts/tegra20.dtsi +0 -0

...

Quoted 4 lines

? ? ? ? ? ? ? compatible = "nvidia,tegra20-i2c";
? ? ? ? ? ? ? reg = <0x7000C000 0x100>;
? ? ? ? ? ? ? interrupts = < 70 >;
+ ? ? ? ? ? ? u-boot,pinmux = <0>;

Quoted 5 lines

That isn't acceptable to me for the same reasons I've outline many times before while discussing USB.

At least periph-id can be argued to be related to the HW, but the pinmux value is a hack that really has no place in device tree.

Are you sure you have this right? The pinmux is set by the hardware, since it is the board schematic which determines which SOC pins are connected to the I2C bus, for example.

There are only a few possible combinations for each port, so I believe it makes sense to represent these by a number, at least until we find out what Linux will do.

But if not, what do you propose we do instead?

Quoted 4 lines
Quoted 2 lines

+ ? ? ? ? ? ? speed = <100000>;

That's already defined as "clock-frequency" in the Linux kernel.

OK thanks. I must be chasing a moving target here, as I got this device tree from the -next branch of the kernel only a few weeks ago. Can you please point me to the latest definition for i2c?

Regards,
Simon

Quoted 6 lines
Quoted 2 lines

+ ? ? ? ? ? ? u-boot,periph-id = <12>; ? ? ? ?// PERIPH_ID_I2C1

--
nvpublic

#8 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

Simon Glass wrote at Monday, December 26, 2011 10:15 PM:

Quoted 18 lines · Stephen Warren
Quoted 11 lines

Simon Glass wrote at Monday, December 26, 2011 11:12 AM:

Quoted 4 lines

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

arch/arm/dts/tegra20.dtsi +0 -0

...

Quoted 4 lines

? ? ? ? ? ? ? compatible = "nvidia,tegra20-i2c";
? ? ? ? ? ? ? reg = <0x7000C000 0x100>;
? ? ? ? ? ? ? interrupts = < 70 >;
+ ? ? ? ? ? ? u-boot,pinmux = <0>;

Quoted 5 lines

That isn't acceptable to me for the same reasons I've outline many times before while discussing USB.

At least periph-id can be argued to be related to the HW, but the pinmux value is a hack that really has no place in device tree.

Quoted 7 lines

Are you sure you have this right? The pinmux is set by the hardware, since it is the board schematic which determines which SOC pins are connected to the I2C bus, for example.

There are only a few possible combinations for each port, so I believe it makes sense to represent these by a number, at least until we find out what Linux will do.

For some peripherals, the number of options is low.

For others, there are quite a few combinations.

Either way, there is no "select pinmux option N" for anything; those "option N" values are something purely internal to the U-Boot driver, and don't even come from tables in the Tegra documentation.

Either way as I've explained before, we can't add a temporary DT binding, then put a proper one in place later, since that will make old .dts files incompatible with new bootloaders or kernels.

Quoted 1 line

But if not, what do you propose we do instead?

Given there's still a lot of flux with DT, I'd suggest adding all the drivers to U-Boot without any DT support at all. That should avoid all the contentious issues. DT support can be added later. Preferably, adding DT support for a given driver would happen at roughly the same time for both U-Boot and the Linux kernel, and get review from DT experts from both development teams. We may have to defer some aspects of DT support quite some time, since areas such as clocks and pinmux may need quite a bit of discussion to get right.

Signature · 1 line
nvpublic
#9 lore raw

PATCH 2/7 tegra: fdt: Add extra I2C definitions for U-Boot

Hi Stephen,

Quoted 3 lines · Stephen Warren

Simon Glass wrote at Monday, December 26, 2011 10:15 PM:

Quoted 1 line

On Mon, Dec 26, 2011 at 8:35 PM, Stephen Warren <[email protected]>

Quoted 11 lines · wrote:
Quoted 11 lines
Quoted 11 lines

Simon Glass wrote at Monday, December 26, 2011 11:12 AM:

Quoted 4 lines

Add U-Boot's peripheral ID and pinmux selection to the Tegra20 device tree file.

arch/arm/dts/tegra20.dtsi +0 -0

...

Quoted 4 lines
              compatible = "nvidia,tegra20-i2c";
              reg = <0x7000C000 0x100>;
              interrupts = < 70 >;

+ u-boot,pinmux = <0>;

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

That isn't acceptable to me for the same reasons I've outline many times before while discussing USB.

At least periph-id can be argued to be related to the HW, but the

pinmux

Quoted 22 lines
Quoted 9 lines
Quoted 1 line

value is a hack that really has no place in device tree.

Are you sure you have this right? The pinmux is set by the hardware, since it is the board schematic which determines which SOC pins are connected to the I2C bus, for example.

There are only a few possible combinations for each port, so I believe it makes sense to represent these by a number, at least until we find out what Linux will do.

For some peripherals, the number of options is low.

For others, there are quite a few combinations.

Either way, there is no "select pinmux option N" for anything; those "option N" values are something purely internal to the U-Boot driver, and don't even come from tables in the Tegra documentation.

Quoted 3 lines

Either way as I've explained before, we can't add a temporary DT binding, then put a proper one in place later, since that will make old .dts files incompatible with new bootloaders or kernels.

Well the fts is in U-Boot so when we change the bindings in U-Boot we change the code. I don't see the problem.

Quoted 11 lines
Quoted 2 lines

But if not, what do you propose we do instead?

Given there's still a lot of flux with DT, I'd suggest adding all the drivers to U-Boot without any DT support at all. That should avoid all the contentious issues. DT support can be added later. Preferably, adding DT support for a given driver would happen at roughly the same time for both U-Boot and the Linux kernel, and get review from DT experts from both development teams. We may have to defer some aspects of DT support quite some time, since areas such as clocks and pinmux may need quite a bit of discussion to get right.

I understand the desire to wait, but this seems less than ideal to me. Why not just make some changes when the kernel makes up its mind?

Look, the fdt is just an agreement between the controller and the user/driver on the bindings to use. I think we are over-thinking this...

Regards,
Simon

Quoted 4 lines

--
nvpublic

#10 lore raw

PATCH 3/7 tegra: Add I2C support to funcmux

In reply to Simon Glass

Add support to funcmux for selecting I2C functions and programming the pinmux appropriately.

---
 arch/arm/cpu/armv7/tegra2/funcmux.c        |   75 +++++++++++++++++++++++-----
 arch/arm/include/asm/arch-tegra2/funcmux.h |    3 +
 2 files changed, 65 insertions(+), 13 deletions(-)
arch/arm/cpu/armv7/tegra2/funcmux.c +62 -13
index 0878f51..82d994a 100644
@@ -26,27 +26,70 @@
 
 int funcmux_select(enum periph_id id, int config)
 {
-	if (config != 0) {
-		debug("%s: invalid config %d for periph_id %d", __func__,
-		      config, id);
-		return -1;
-	}
+	int bad_config = config != 0;
+
 	switch (id) {
 	case PERIPH_ID_UART1:
-		pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA);
-		pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA);
-		pinmux_tristate_disable(PINGRP_IRRX);
-		pinmux_tristate_disable(PINGRP_IRTX);
+		if (config == 0) {
+			pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA);
+			pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA);
+			pinmux_tristate_disable(PINGRP_IRRX);
+			pinmux_tristate_disable(PINGRP_IRTX);
+		}
 		break;
 
 	case PERIPH_ID_UART2:
-		pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA);
-		pinmux_tristate_disable(PINGRP_UAD);
+		if (config == 0) {
+			pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA);
+			pinmux_tristate_disable(PINGRP_UAD);
+		}
 		break;
 
 	case PERIPH_ID_UART4:
-		pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD);
-		pinmux_tristate_disable(PINGRP_GMC);
+		if (config == 0) {
+			pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD);
+			pinmux_tristate_disable(PINGRP_GMC);
+		}
+		break;
+
+	case PERIPH_ID_DVC_I2C:
+		/* there is only one selection, pinmux_config is ignored */
+		if (config == 0) {
+			pinmux_set_func(PINGRP_I2CP, PMUX_FUNC_I2C);
+			pinmux_tristate_disable(PINGRP_I2CP);
+		}
+		break;
+
+	case PERIPH_ID_I2C1:
+		/* support pinmux_config of 0 for now, */
+		if (config == 0) {
+			pinmux_set_func(PINGRP_RM, PMUX_FUNC_I2C);
+			pinmux_tristate_disable(PINGRP_RM);
+		}
+		break;
+	case PERIPH_ID_I2C2: /* I2C2 */
+		switch (config) {
+		case 0:	/* DDC pin group, select I2C2 */
+			pinmux_set_func(PINGRP_DDC, PMUX_FUNC_I2C2);
+			/* PTA to HDMI */
+			pinmux_set_func(PINGRP_PTA, PMUX_FUNC_HDMI);
+			pinmux_tristate_disable(PINGRP_DDC);
+			break;
+		case 1:	/* PTA pin group, select I2C2 */
+			pinmux_set_func(PINGRP_PTA, PMUX_FUNC_I2C2);
+			/* set DDC_SEL to RSVDx (RSVD2 works for now) */
+			pinmux_set_func(PINGRP_DDC, PMUX_FUNC_RSVD2);
+			pinmux_tristate_disable(PINGRP_PTA);
+			bad_config = 0;
+			break;
+		}
+		break;
+	case PERIPH_ID_I2C3: /* I2C3 */
+		/* support pinmux_config of 0 for now */
+		if (config == 0) {
+			pinmux_set_func(PINGRP_DTF, PMUX_FUNC_I2C3);
+			pinmux_tristate_disable(PINGRP_DTF);
+		}
 		break;
 
 	default:
@@ -54,5 +97,11 @@ int funcmux_select(enum periph_id id, int config)
 		return -1;
 	}
 
+	if (bad_config) {
+		debug("%s: invalid config %d for periph_id %d", __func__,
+		      config, id);
+		return -1;
+	}
+
 	return 0;
 }
arch/arm/include/asm/arch-tegra2/funcmux.h +3 -0
index 2d724a2..d4f9cfb 100644
@@ -32,6 +32,9 @@
  * The basic config is 0, and higher numbers indicate different
  * pinmux settings to bring the peripheral out on other pins,
  *
+ * This function also disables tristate for the function's pins,
+ * so that they operate in normal mode.
+ *
  * @param id		Peripheral id
  * @param config	Configuration to use (generally 0)
  * @return 0 if ok, -1 on error (e.g. incorrect id or config)
Signature · 1 line
1.7.3.1
#11 lore raw

PATCH 3/7 tegra: Add I2C support to funcmux

Quoted 4 lines · Simon Glass

Add support to funcmux for selecting I2C functions and programming the pinmux appropriately.

Conceptually the changes here look fine. I think that adding the new I2C cases and the tests for valid config values to the existing cases are probably worthing of two separate patches though.

Signature · 1 line
nvpublic
#12 lore raw

PATCH 3/7 tegra: Add I2C support to funcmux

Hi Stephen,

Quoted 9 lines · Stephen Warren
Quoted 4 lines · Simon Glass

Add support to funcmux for selecting I2C functions and programming the pinmux appropriately.

Conceptually the changes here look fine. I think that adding the new I2C cases and the tests for valid config values to the existing cases are probably worthing of two separate patches though.

OK, I might do this as part of the mmc side.

Regards,
Simon

Quoted 3 lines

--
nvpublic

#13 lore raw

PATCH 3/7 tegra: Add I2C support to funcmux

Hi Stephe,

Quoted 7 lines · Simon Glass

Hi Stephen,

Quoted 4 lines · Stephen Warren
Quoted 3 lines · Simon Glass

Add support to funcmux for selecting I2C functions and programming the pinmux appropriately.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Conceptually the changes here look fine. I think that adding the new I2C cases and the tests for valid config values to the existing cases are probably worthing of two separate patches though.

OK, I might do this as part of the mmc side.

Since there are a few things there now I have created a separate funcmux series and moved this I2C patch into that. I will drop it from here when I send the next version.

Regards,
Simon

Quoted 7 lines

Regards,
Simon

Quoted 3 lines

--
nvpublic

#14 lore raw

PATCH 4/7 tegra: Add I2C driver

In reply to Simon Glass

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

---
 arch/arm/include/asm/arch-tegra2/tegra2.h     |    4 +
 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h |  167 ++++++++
 drivers/i2c/Makefile                          |    1 +
 drivers/i2c/tegra2_i2c.c                      |  533 +++++++++++++++++++++++++
 include/fdtdec.h                              |    1 +
 lib/fdtdec.c                                  |    1 +
 6 files changed, 707 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
 create mode 100644 drivers/i2c/tegra2_i2c.c
arch/arm/include/asm/arch-tegra2/tegra2.h +4 -0
index ca1881e..d1a70da 100644
@@ -40,6 +40,10 @@
 #define NV_PA_APB_UARTE_BASE	(NV_PA_APB_MISC_BASE + 0x6400)
 #define TEGRA2_SPI_BASE		(NV_PA_APB_MISC_BASE + 0xC380)
 #define TEGRA2_PMC_BASE		(NV_PA_APB_MISC_BASE + 0xE400)
+#define TEGRA2_I2C1_BASE	(NV_PA_APB_MISC_BASE + 0xC000)
+#define TEGRA2_I2C2_BASE	(NV_PA_APB_MISC_BASE + 0xC400)
+#define TEGRA2_I2C3_BASE	(NV_PA_APB_MISC_BASE + 0xC500)
+#define TEGRA2_DVC_BASE		(NV_PA_APB_MISC_BASE + 0xD000)
 #define NV_PA_CSITE_BASE	0x70040000
 #define TEGRA_USB1_BASE		0xC5000000
 #define TEGRA_USB3_BASE		0xC5008000
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +167 -0

167 lines not on this page · raw message

drivers/i2c/Makefile +1 -0
index 504db03..c123c72 100644
@@ -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_TEGRA2_I2C) += tegra2_i2c.o
 COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
 COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
 COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
drivers/i2c/tegra2_i2c.c new file +533 -0

533 lines not on this page · raw message

include/fdtdec.h +1 -0
index a8911b5..5547676 100644
@@ -58,6 +58,7 @@ struct fdt_memory {
 enum fdt_compat_id {
 	COMPAT_UNKNOWN,
 	COMPAT_NVIDIA_TEGRA20_USB,	/* Tegra2 USB port */
+	COMPAT_NVIDIA_TEGRA20_I2C,	/* Tegra2 i2c */
 
 	COMPAT_COUNT,
 };
lib/fdtdec.c +1 -0
index 931b4ce..fb3d79d 100644
@@ -38,6 +38,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_TEGRA20_I2C, "nvidia,tegra20-i2c"),
 };
 
 /**
Signature · 1 line
1.7.3.1
#15 lore raw

PATCH 4/7 tegra: Add I2C driver

Quoted 68 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

---
 arch/arm/include/asm/arch-tegra2/tegra2.h     |    4 +
 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h |  167 ++++++++
 drivers/i2c/Makefile                          |    1 +
 drivers/i2c/tegra2_i2c.c                      |  533
+++++++++++++++++++++++++ include/fdtdec.h                              | 
  1 +
 lib/fdtdec.c                                  |    1 +
 6 files changed, 707 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
 create mode 100644 drivers/i2c/tegra2_i2c.c
(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2.h index ca1881e..d1a70da 100644

arch/arm/include/asm/arch-tegra2/tegra2.h +4 -0
@@ -40,6 +40,10 @@
 #define NV_PA_APB_UARTE_BASE	(NV_PA_APB_MISC_BASE + 0x6400)
 #define TEGRA2_SPI_BASE		(NV_PA_APB_MISC_BASE + 0xC380)
 #define TEGRA2_PMC_BASE		(NV_PA_APB_MISC_BASE + 0xE400)
+#define TEGRA2_I2C1_BASE	(NV_PA_APB_MISC_BASE + 0xC000)
+#define TEGRA2_I2C2_BASE	(NV_PA_APB_MISC_BASE + 0xC400)
+#define TEGRA2_I2C3_BASE	(NV_PA_APB_MISC_BASE + 0xC500)
+#define TEGRA2_DVC_BASE		(NV_PA_APB_MISC_BASE + 0xD000)
 #define NV_PA_CSITE_BASE	0x70040000
 #define TEGRA_USB1_BASE		0xC5000000
 #define TEGRA_USB3_BASE		0xC5008000
(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file mode 100644 index 0000000..4920d47

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +21 -0
@@ -0,0 +1,167 @@
+/*
+ * 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,

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 31

Quoted 10 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +10 -0
+ * MA 02111-1307 USA
+ */
+
+#ifndef _TEGRA2_I2C_H_
+#define _TEGRA2_I2C_H_
+
+#include <asm/types.h>
+
+/* Convert the number of bytes to word. */
+#define BYTES_TO_WORDS(size)		(((size) + 3) >> 2)

Isn't this stuff in include/common.h ? div_round_up() or something ?

Quoted 21 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +21 -0
+
+/* Convert i2c slave address to be put on bus  */
+#define I2C_ADDR_ON_BUS(chip)		(chip << 1)
+
+#ifndef CONFIG_OF_CONTROL
+enum {
+	I2CSPEED_KHZ = 100,		/* in KHz */
+};
+#endif
+
+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 */

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 62

Quoted 10 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +10 -0
+	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 I2C send operation this buffer should be

Wrong comment, do you run this stuff through checkpatch ?

Quoted 162 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +103 -0
+	 * filled with the data to be sent to the slave device. For 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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +1 -0
+#endif
drivers/i2c/Makefile +1 -0
index 504db03..c123c72 100644
@@ -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_TEGRA2_I2C) += tegra2_i2c.o
 COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
 COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
 COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
drivers/i2c/tegra2_i2c.c new file +39 -0
new file mode 100644
index 0000000..ae43531
@@ -0,0 +1,533 @@
+/*
+ * 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/tegra2_i2c.h>
+#include <fdtdec.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static unsigned int i2c_bus_num;
+
+/* Information about i2c controller */

drivers/i2c/tegra2_i2c.c line 49

Quoted 10 lines
drivers/i2c/tegra2_i2c.c new file +10 -0
+struct i2c_bus {
+	int			id;
+	enum periph_id		periph_id;
+	int			speed;
+	int			pinmux_config;
+	struct i2c_control	*control;
+	struct i2c_ctlr		*regs;
+};
+
+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

static

Quoted 474 lines
drivers/i2c/tegra2_i2c.c new file +474 -0
+
+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->periph_id == PERIPH_ID_DVC_I2C) {
+		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)
+{
+	/* TODO: Fix bug which makes us need to do this */
+	clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_OSC,
+			       i2c_bus->speed * (8 * 2 - 1));
+
+	/* Reset I2C controller. */
+	i2c_reset_controller(i2c_bus);
+
+	/* Configure I2C controller. */
+	if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C) {	/* 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 = BYTES_TO_WORDS(trans->num_bytes);
+	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
+static const enum periph_id i2c_periph_ids[CONFIG_SYS_MAX_I2C_BUS] = {
+	PERIPH_ID_DVC_I2C,
+	PERIPH_ID_I2C1,
+	PERIPH_ID_I2C2,
+	PERIPH_ID_I2C3
+};
+
+static const u32 *i2c_bus_base[CONFIG_SYS_MAX_I2C_BUS] = {
+	(u32 *)TEGRA2_DVC_BASE,
+	(u32 *)TEGRA2_I2C1_BASE,
+	(u32 *)TEGRA2_I2C2_BASE,
+	(u32 *)TEGRA2_I2C3_BASE
+};
+
+/* pinmux_configs based on the pinmux configuration */
+static const int pinmux_configs[CONFIG_SYS_MAX_I2C_BUS] = {
+	CONFIG_I2CP_PIN_MUX,	/* for I2CP (DVC I2C) */
+	CONFIG_I2C1_PIN_MUX,	/* for I2C1 */
+	CONFIG_I2C2_PIN_MUX,	/* for I2C2 */
+	CONFIG_I2C3_PIN_MUX	/* for I2C3 */
+};
+
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+	int i = *index;
+
+	if (i >= CONFIG_SYS_MAX_I2C_BUS)
+		return -1;
+
+	i2c_bus->periph_id = i2c_periph_ids[i];
+	i2c_bus->pinmux_config = pinmux_configs[i];
+	i2c_bus->regs = (struct i2c_ctlr *)i2c_bus_base[i];
+	i2c_bus->speed = I2CSPEED_KHZ * 1000;
+
+	*index = i + 1;
+
+	return 0;
+}
+#else
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+	const void *blob = gd->fdt_blob;
+	int node;
+
+	node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
+				 index);
+	if (node < 0)
+		return -1;
+
+	i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg");
+	i2c_bus->pinmux_config = fdtdec_get_int(blob, node, "u-boot,pinmux", 0);
+	i2c_bus->speed = fdtdec_get_int(blob, node, "speed", 0);
+	i2c_bus->periph_id = fdtdec_get_int(blob, node, "u-boot,periph-id", -1);
+
+	if (i2c_bus->periph_id == -1)
+		return -FDT_ERR_NOTFOUND;
+
+	return 0;
+}
+#endif
+
+int i2c_init_board(void)
+{
+	struct i2c_bus *i2c_bus;
+	int index = 0;
+	int i;
+
+	/* build the i2c_controllers[] for each controller */
+	for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+		i2c_bus = &i2c_controllers[i];
+		i2c_bus->id = i;
+
+		if (i2c_get_config(&index, i2c_bus)) {
+			printf("i2c_init_board: failed to find bus %d\n", i);
+			return -1;
+		}
+
+		if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+			i2c_bus->control =
+				&((struct dvc_ctlr *)i2c_bus->regs)->control;
+		else
+			i2c_bus->control = &i2c_bus->regs->control;
+
+		i2c_init_controller(i2c_bus);
+	}
+
+	return 0;
+}
+
+void i2c_init(int speed, int slaveaddr)
+{
+	debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}
+
+/* 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, &reg, 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)
+{
+	if (alen < 0 || alen > sizeof(addr))
+		return 0;
+	if (alen != sizeof(addr)) {
+		uint max_addr = (1 << (8 * alen)) - 1;
+		if (addr > max_addr)
+			return 0;
+	}
+	return 1;
+}
+
+/* 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;
+}

drivers/i2c/tegra2_i2c.c line 533

Quoted 10 lines
drivers/i2c/tegra2_i2c.c new file +10 -0
+
+int i2c_set_bus_num(unsigned int bus)
+{
+	if (bus >= CONFIG_SYS_MAX_I2C_BUS)
+		return -1;
+	i2c_bus_num = bus;
+
+	return 0;
+}
+#endif

get/set_speed missing ?

Quoted 17 lines
include/fdtdec.h +1 -0
index a8911b5..5547676 100644
@@ -58,6 +58,7 @@ struct fdt_memory {
 enum fdt_compat_id {
 	COMPAT_UNKNOWN,
 	COMPAT_NVIDIA_TEGRA20_USB,	/* Tegra2 USB port */
+	COMPAT_NVIDIA_TEGRA20_I2C,	/* Tegra2 i2c */
 
 	COMPAT_COUNT,
 };
lib/fdtdec.c +0 -0
index 931b4ce..fb3d79d 100644

lib/fdtdec.c DECLARE_GLOBAL_DATA_PTR; · line 44

Quoted 7 lines
lib/fdtdec.c +1 -0
@@ -38,6 +38,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_TEGRA20_I2C, "nvidia,tegra20-i2c"),
 };
 
 /**

M

#16 lore raw

PATCH 4/7 tegra: Add I2C driver

Hi Marek,

Quoted 105 lines · Marek Vasut
Quoted 68 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3) ? ? ? (typically this will be 0 to bring the port out the common ? ? ? ? ? ? ? pins)

---

?arch/arm/include/asm/arch-tegra2/tegra2.h ? ? | ? ?4 +
?arch/arm/include/asm/arch-tegra2/tegra2_i2c.h | ?167 ++++++++
?drivers/i2c/Makefile ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?1 +
?drivers/i2c/tegra2_i2c.c ? ? ? ? ? ? ? ? ? ? ?| ?533

+++++++++++++++++++++++++ include/fdtdec.h ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? 1 +
?lib/fdtdec.c ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?1 +
?6 files changed, 707 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h ?create mode 100644 drivers/i2c/tegra2_i2c.c

(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2.h index ca1881e..d1a70da 100644

arch/arm/include/asm/arch-tegra2/tegra2.h +4 -0
@@ -40,6 +40,10 @@
 ?#define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400)
 ?#define TEGRA2_SPI_BASE ? ? ? ? ? ? ?(NV_PA_APB_MISC_BASE + 0xC380)
 ?#define TEGRA2_PMC_BASE ? ? ? ? ? ? ?(NV_PA_APB_MISC_BASE + 0xE400)
+#define TEGRA2_I2C1_BASE ? ? (NV_PA_APB_MISC_BASE + 0xC000)
+#define TEGRA2_I2C2_BASE ? ? (NV_PA_APB_MISC_BASE + 0xC400)
+#define TEGRA2_I2C3_BASE ? ? (NV_PA_APB_MISC_BASE + 0xC500)
+#define TEGRA2_DVC_BASE ? ? ? ? ? ? ?(NV_PA_APB_MISC_BASE + 0xD000)

?#define NV_PA_CSITE_BASE ? ? 0x70040000
?#define TEGRA_USB1_BASE ? ? ? ? ? ? ?0xC5000000
?#define TEGRA_USB3_BASE ? ? ? ? ? ? ?0xC5008000

(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file mode 100644 index 0000000..4920d47

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +21 -0
@@ -0,0 +1,167 @@
+/*
+ * 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,

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 31

Quoted 10 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +10 -0
+ * MA 02111-1307 USA
+ */
+
+#ifndef _TEGRA2_I2C_H_
+#define _TEGRA2_I2C_H_
+
+#include <asm/types.h>
+
+/* Convert the number of bytes to word. */
+#define BYTES_TO_WORDS(size) ? ? ? ? (((size) + 3) >> 2)

Isn't this stuff in include/common.h ? div_round_up() or something ?

Quoted 25 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +25 -0
+
+/* Convert i2c slave address to be put on bus ?*/
+#define I2C_ADDR_ON_BUS(chip) ? ? ? ? ? ? ? ?(chip << 1)
+
+#ifndef CONFIG_OF_CONTROL
+enum {
+ ? ? I2CSPEED_KHZ = 100, ? ? ? ? ? ? /* in KHz */
+};
+#endif
+
+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 */
Quoted 8 lines

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 62

Quoted 6 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +6 -0
+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 I2C send operation this buffer should be

Wrong comment, do you run this stuff through checkpatch ?

Fixed style and adjusted comment. Yes I do, but checkpatch isn't perfect.

Quoted 167 lines
Quoted 167 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +103 -0
+ ? ? ?* filled with the data to be sent to the slave device. For 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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +1 -0
+#endif
drivers/i2c/Makefile +0 -0
index 504db03..c123c72 100644
@@ -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_TEGRA2_I2C) += tegra2_i2c.o
?COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
?COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
?COBJS-$(CONFIG_SH_I2C) += sh_i2c.o

drivers/i2c/tegra2_i2c.c new file +43 -0
new file mode 100644
index 0000000..ae43531
@@ -0,0 +1,533 @@
+/*
+ * 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/tegra2_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;
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 49

Quoted 6 lines
drivers/i2c/tegra2_i2c.c new file +6 -0
+ ? ? int ? ? ? ? ? ? ? ? ? ? pinmux_config;
+ ? ? struct i2c_control ? ? ?*control;
+ ? ? struct i2c_ctlr ? ? ? ? *regs;
+};
+
+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

static

done

Quoted 479 lines
Quoted 479 lines
drivers/i2c/tegra2_i2c.c new file +478 -0
+
+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->periph_id == PERIPH_ID_DVC_I2C) {
+ ? ? ? ? ? ? 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)
+{
+ ? ? /* TODO: Fix bug which makes us need to do this */
+ ? ? clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_OSC,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ?i2c_bus->speed * (8 * 2 - 1));
+
+ ? ? /* Reset I2C controller. */
+ ? ? i2c_reset_controller(i2c_bus);
+
+ ? ? /* Configure I2C controller. */
+ ? ? if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C) { ?/* 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 = BYTES_TO_WORDS(trans->num_bytes);
+ ? ? 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
+static const enum periph_id i2c_periph_ids[CONFIG_SYS_MAX_I2C_BUS] = {
+ ? ? PERIPH_ID_DVC_I2C,
+ ? ? PERIPH_ID_I2C1,
+ ? ? PERIPH_ID_I2C2,
+ ? ? PERIPH_ID_I2C3
+};
+
+static const u32 *i2c_bus_base[CONFIG_SYS_MAX_I2C_BUS] = {
+ ? ? (u32 *)TEGRA2_DVC_BASE,
+ ? ? (u32 *)TEGRA2_I2C1_BASE,
+ ? ? (u32 *)TEGRA2_I2C2_BASE,
+ ? ? (u32 *)TEGRA2_I2C3_BASE
+};
+
+/* pinmux_configs based on the pinmux configuration */
+static const int pinmux_configs[CONFIG_SYS_MAX_I2C_BUS] = {
+ ? ? CONFIG_I2CP_PIN_MUX, ? ?/* for I2CP (DVC I2C) */
+ ? ? CONFIG_I2C1_PIN_MUX, ? ?/* for I2C1 */
+ ? ? CONFIG_I2C2_PIN_MUX, ? ?/* for I2C2 */
+ ? ? CONFIG_I2C3_PIN_MUX ? ? /* for I2C3 */
+};
+
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+ ? ? int i = *index;
+
+ ? ? if (i >= CONFIG_SYS_MAX_I2C_BUS)
+ ? ? ? ? ? ? return -1;
+
+ ? ? i2c_bus->periph_id = i2c_periph_ids[i];
+ ? ? i2c_bus->pinmux_config = pinmux_configs[i];
+ ? ? i2c_bus->regs = (struct i2c_ctlr *)i2c_bus_base[i];
+ ? ? i2c_bus->speed = I2CSPEED_KHZ * 1000;
+
+ ? ? *index = i + 1;
+
+ ? ? return 0;
+}
+#else
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+ ? ? const void *blob = gd->fdt_blob;
+ ? ? int node;
+
+ ? ? node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?index);
+ ? ? if (node < 0)
+ ? ? ? ? ? ? return -1;
+
+ ? ? i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg");
+ ? ? i2c_bus->pinmux_config = fdtdec_get_int(blob, node, "u-boot,pinmux", 0);
+ ? ? i2c_bus->speed = fdtdec_get_int(blob, node, "speed", 0);
+ ? ? i2c_bus->periph_id = fdtdec_get_int(blob, node, "u-boot,periph-id", -1);
+
+ ? ? if (i2c_bus->periph_id == -1)
+ ? ? ? ? ? ? return -FDT_ERR_NOTFOUND;
+
+ ? ? return 0;
+}
+#endif
+
+int i2c_init_board(void)
+{
+ ? ? struct i2c_bus *i2c_bus;
+ ? ? int index = 0;
+ ? ? int i;
+
+ ? ? /* build the i2c_controllers[] for each controller */
+ ? ? for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+ ? ? ? ? ? ? i2c_bus = &i2c_controllers[i];
+ ? ? ? ? ? ? i2c_bus->id = i;
+
+ ? ? ? ? ? ? if (i2c_get_config(&index, i2c_bus)) {
+ ? ? ? ? ? ? ? ? ? ? printf("i2c_init_board: failed to find bus %d\n", i);
+ ? ? ? ? ? ? ? ? ? ? return -1;
+ ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+ ? ? ? ? ? ? ? ? ? ? i2c_bus->control =
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? &((struct dvc_ctlr *)i2c_bus->regs)->control;
+ ? ? ? ? ? ? else
+ ? ? ? ? ? ? ? ? ? ? i2c_bus->control = &i2c_bus->regs->control;
+
+ ? ? ? ? ? ? i2c_init_controller(i2c_bus);
+ ? ? }
+
+ ? ? return 0;
+}
+
+void i2c_init(int speed, int slaveaddr)
+{
+ ? ? debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}
+
+/* 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, &reg, 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)
+{
+ ? ? if (alen < 0 || alen > sizeof(addr))
+ ? ? ? ? ? ? return 0;
+ ? ? if (alen != sizeof(addr)) {
+ ? ? ? ? ? ? uint max_addr = (1 << (8 * alen)) - 1;
+ ? ? ? ? ? ? if (addr > max_addr)
+ ? ? ? ? ? ? ? ? ? ? return 0;
+ ? ? }
+ ? ? return 1;
+}
+
+/* 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 >= CONFIG_SYS_MAX_I2C_BUS)
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 353

Quoted 6 lines
drivers/i2c/tegra2_i2c.c new file +6 -0
+ ? ? ? ? ? ? return -1;
+ ? ? i2c_bus_num = bus;
+
+ ? ? return 0;
+}
+#endif

get/set_speed missing ?

This was done during init, so hoping we don't have to do it every time?

Quoted 24 lines
Quoted 24 lines
include/fdtdec.h +0 -0
index a8911b5..5547676 100644
@@ -58,6 +58,7 @@ struct fdt_memory {
 ?enum fdt_compat_id {
 ? ? ? COMPAT_UNKNOWN,

? ? ? COMPAT_NVIDIA_TEGRA20_USB, ? ? ?/* Tegra2 USB port */
+ ? ? COMPAT_NVIDIA_TEGRA20_I2C, ? ? ?/* Tegra2 i2c */

? ? ? COMPAT_COUNT,
?};

lib/fdtdec.c +1 -0
index 931b4ce..fb3d79d 100644
@@ -38,6 +38,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_TEGRA20_I2C, "nvidia,tegra20-i2c"),
 ?};
 
Quoted 2 lines
Quoted 1 line

?/**

M

Regards,
Simon

#17 lore raw

PATCH 4/7 tegra: Add I2C driver

Quoted 790 lines

Hi Marek,

Quoted 107 lines · Marek Vasut
Quoted 68 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

---
 arch/arm/include/asm/arch-tegra2/tegra2.h     |    4 +
 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h |  167 ++++++++
 drivers/i2c/Makefile                          |    1 +
 drivers/i2c/tegra2_i2c.c                      |  533
+++++++++++++++++++++++++ include/fdtdec.h                            
 | 1 +
 lib/fdtdec.c                                  |    1 +
 6 files changed, 707 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
 create mode 100644 drivers/i2c/tegra2_i2c.c
(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2.h index ca1881e..d1a70da 100644 --- a/arch/arm/include/asm/arch-tegra2/tegra2.h +++ b/arch/arm/include/asm/arch-tegra2/tegra2.h

arch/arm/include/asm/arch-tegra2/tegra2.h +4 -0
@@ -40,6 +40,10 @@
 #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400)
 #define TEGRA2_SPI_BASE              (NV_PA_APB_MISC_BASE + 0xC380)
 #define TEGRA2_PMC_BASE              (NV_PA_APB_MISC_BASE + 0xE400)
+#define TEGRA2_I2C1_BASE     (NV_PA_APB_MISC_BASE + 0xC000)
+#define TEGRA2_I2C2_BASE     (NV_PA_APB_MISC_BASE + 0xC400)
+#define TEGRA2_I2C3_BASE     (NV_PA_APB_MISC_BASE + 0xC500)
+#define TEGRA2_DVC_BASE              (NV_PA_APB_MISC_BASE + 0xD000)
 #define NV_PA_CSITE_BASE     0x70040000
 #define TEGRA_USB1_BASE              0xC5000000
 #define TEGRA_USB3_BASE              0xC5008000
(quoted diff) +0 -0

b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file mode 100644 index 0000000..4920d47

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +21 -0
@@ -0,0 +1,167 @@
+/*
+ * 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,

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 31

Quoted 10 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +10 -0
+ * MA 02111-1307 USA
+ */
+
+#ifndef _TEGRA2_I2C_H_
+#define _TEGRA2_I2C_H_
+
+#include <asm/types.h>
+
+/* Convert the number of bytes to word. */
+#define BYTES_TO_WORDS(size)         (((size) + 3) >> 2)

Isn't this stuff in include/common.h ? div_round_up() or something ?

Quoted 26 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +20 -0
+
+/* Convert i2c slave address to be put on bus  */
+#define I2C_ADDR_ON_BUS(chip)                (chip << 1)
+
+#ifndef CONFIG_OF_CONTROL
+enum {
+     I2CSPEED_KHZ = 100,             /* in KHz */
+};
+#endif
+
+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,

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +4 -0
+};
+
+/* Contians the I2C transaction details */
+struct i2c_trans_info {
Quoted 8 lines
Quoted 6 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +5 -0
+     /* 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 I2C send operation this buffer should

be

Wrong comment, do you run this stuff through checkpatch ?

Fixed style and adjusted comment. Yes I do, but checkpatch isn't perfect.

Quoted 168 lines
Quoted 168 lines

+ * filled with the data to be sent to the slave device. For I2C
receive + * operation this buffer is filled with the data received
from the + * slave device. */

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +57 -0
+     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

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +6 -0
+#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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +3 -0
+/* 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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +7 -0
+#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

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +5 -0
+#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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +3 -0
+/* 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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +7 -0
+/* 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) +

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file +1 -0
+#endif
drivers/i2c/Makefile +1 -0
index 504db03..c123c72 100644
@@ -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_TEGRA2_I2C) += tegra2_i2c.o
 COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
 COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
 COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
drivers/i2c/tegra2_i2c.c new file +43 -0
new file mode 100644
index 0000000..ae43531
@@ -0,0 +1,533 @@
+/*
+ * 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/tegra2_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;
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 49

Quoted 6 lines
drivers/i2c/tegra2_i2c.c new file +6 -0
+     int                     pinmux_config;
+     struct i2c_control      *control;
+     struct i2c_ctlr         *regs;
+};
+
+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

static

done

Quoted 481 lines
Quoted 481 lines
drivers/i2c/tegra2_i2c.c new file +18 -0
+
+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->periph_id == PERIPH_ID_DVC_I2C) {
+             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); + }

drivers/i2c/tegra2_i2c.c new file +22 -0
+}
+
+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)
+{
+     /* TODO: Fix bug which makes us need to do this */
+     clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_OSC,
+                            i2c_bus->speed * (8 * 2 - 1));
+
+     /* Reset I2C controller. */
+     i2c_reset_controller(i2c_bus);
+
+     /* Configure I2C controller. */
+     if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C) {  /* 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); + }

drivers/i2c/tegra2_i2c.c new file +42 -0
+
+     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;

drivers/i2c/tegra2_i2c.c new file +16 -0
+             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;

drivers/i2c/tegra2_i2c.c new file +44 -0
+             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);

drivers/i2c/tegra2_i2c.c new file +18 -0
+     writel(int_status, &control->int_status);
+
+     send_packet_headers(i2c_bus, trans, 1);
+
+     words = BYTES_TO_WORDS(trans->num_bytes);
+     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); + }

drivers/i2c/tegra2_i2c.c new file +11 -0
+                     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. + */

drivers/i2c/tegra2_i2c.c new file +37 -0
+                     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)

drivers/i2c/tegra2_i2c.c new file +17 -0
+             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)

drivers/i2c/tegra2_i2c.c new file +56 -0
+             debug("tegra2_i2c_read_data: Error (%d) !!!\n", error);
+
+     return error;
+}
+
+#ifndef CONFIG_OF_CONTROL
+static const enum periph_id i2c_periph_ids[CONFIG_SYS_MAX_I2C_BUS] = {
+     PERIPH_ID_DVC_I2C,
+     PERIPH_ID_I2C1,
+     PERIPH_ID_I2C2,
+     PERIPH_ID_I2C3
+};
+
+static const u32 *i2c_bus_base[CONFIG_SYS_MAX_I2C_BUS] = {
+     (u32 *)TEGRA2_DVC_BASE,
+     (u32 *)TEGRA2_I2C1_BASE,
+     (u32 *)TEGRA2_I2C2_BASE,
+     (u32 *)TEGRA2_I2C3_BASE
+};
+
+/* pinmux_configs based on the pinmux configuration */
+static const int pinmux_configs[CONFIG_SYS_MAX_I2C_BUS] = {
+     CONFIG_I2CP_PIN_MUX,    /* for I2CP (DVC I2C) */
+     CONFIG_I2C1_PIN_MUX,    /* for I2C1 */
+     CONFIG_I2C2_PIN_MUX,    /* for I2C2 */
+     CONFIG_I2C3_PIN_MUX     /* for I2C3 */
+};
+
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+     int i = *index;
+
+     if (i >= CONFIG_SYS_MAX_I2C_BUS)
+             return -1;
+
+     i2c_bus->periph_id = i2c_periph_ids[i];
+     i2c_bus->pinmux_config = pinmux_configs[i];
+     i2c_bus->regs = (struct i2c_ctlr *)i2c_bus_base[i];
+     i2c_bus->speed = I2CSPEED_KHZ * 1000;
+
+     *index = i + 1;
+
+     return 0;
+}
+#else
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+     const void *blob = gd->fdt_blob;
+     int node;
+
+     node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
+                              index);
+     if (node < 0)
+             return -1;
+
+     i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node,

"reg"); + i2c_bus->pinmux_config = fdtdec_get_int(blob, node,
"u-boot,pinmux", 0); + i2c_bus->speed = fdtdec_get_int(blob, node,
"speed", 0);
+ i2c_bus->periph_id = fdtdec_get_int(blob, node,
"u-boot,periph-id", -1); +

drivers/i2c/tegra2_i2c.c new file +20 -0
+     if (i2c_bus->periph_id == -1)
+             return -FDT_ERR_NOTFOUND;
+
+     return 0;
+}
+#endif
+
+int i2c_init_board(void)
+{
+     struct i2c_bus *i2c_bus;
+     int index = 0;
+     int i;
+
+     /* build the i2c_controllers[] for each controller */
+     for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+             i2c_bus = &i2c_controllers[i];
+             i2c_bus->id = i;
+
+             if (i2c_get_config(&index, i2c_bus)) {
+                     printf("i2c_init_board: failed to find bus %d\n",

i); + return -1;

drivers/i2c/tegra2_i2c.c new file +5 -0
+             }
+
+             if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+                     i2c_bus->control =
+                             &((struct dvc_ctlr

*)i2c_bus->regs)->control; + else

drivers/i2c/tegra2_i2c.c new file +153 -0
+                     i2c_bus->control = &i2c_bus->regs->control;
+
+             i2c_init_controller(i2c_bus);
+     }
+
+     return 0;
+}
+
+void i2c_init(int speed, int slaveaddr)
+{
+     debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}
+
+/* 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, &reg, 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)
+{
+     if (alen < 0 || alen > sizeof(addr))
+             return 0;
+     if (alen != sizeof(addr)) {
+             uint max_addr = (1 << (8 * alen)) - 1;
+             if (addr > max_addr)
+                     return 0;
+     }
+     return 1;
+}
+
+/* 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 >= CONFIG_SYS_MAX_I2C_BUS)
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 533

Quoted 6 lines
drivers/i2c/tegra2_i2c.c new file +6 -0
+             return -1;
+     i2c_bus_num = bus;
+
+     return 0;
+}
+#endif

get/set_speed missing ?

Quoted 1 line

This was done during init, so hoping we don't have to do it every time?

No, but you can use those functions to adjust the speed on the fly (there's i2c speed ... command in uboot)

Quoted 30 lines
Quoted 24 lines
Quoted 24 lines
include/fdtdec.h +1 -0
index a8911b5..5547676 100644
@@ -58,6 +58,7 @@ struct fdt_memory {
 enum fdt_compat_id {
      COMPAT_UNKNOWN,
      COMPAT_NVIDIA_TEGRA20_USB,      /* Tegra2 USB port */
+     COMPAT_NVIDIA_TEGRA20_I2C,      /* Tegra2 i2c */
 
      COMPAT_COUNT,
 };
lib/fdtdec.c +1 -0
index 931b4ce..fb3d79d 100644
@@ -38,6 +38,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_TEGRA20_I2C, "nvidia,tegra20-i2c"),
 };
 
Quoted 3 lines

lib/fdtdec.c DECLARE_GLOBAL_DATA_PTR; · line 44

Quoted 1 line
lib/fdtdec.c +0 -0
 /**

M

Regards,
Simon

#18 lore raw

PATCH 4/7 tegra: Add I2C driver

Hi Marek,

Quoted 1 line · Marek Vasut
Quoted 1 line

Hi Marek,

[snip]

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

drivers/i2c/tegra2_i2c.c line 533

Quoted 10 lines
drivers/i2c/tegra2_i2c.c new file +10 -0
+
+int i2c_set_bus_num(unsigned int bus)
+{
+ ? ? if (bus >= CONFIG_SYS_MAX_I2C_BUS)
+ ? ? ? ? ? ? return -1;
+ ? ? i2c_bus_num = bus;
+
+ ? ? return 0;
+}
+#endif

get/set_speed missing ?

Quoted 4 lines
Quoted 1 line

This was done during init, so hoping we don't have to do it every time?

No, but you can use those functions to adjust the speed on the fly (there's i2c speed ... command in uboot)

OK I see. I will take a look at implementing i2c_set_bus_speed() and i2c_get_bus_speed().

Regards,
Simon

Quoted 30 lines
Quoted 30 lines
Quoted 24 lines
Quoted 24 lines
include/fdtdec.h +0 -0
index a8911b5..5547676 100644
@@ -58,6 +58,7 @@ struct fdt_memory {
 ?enum fdt_compat_id {
 ? ? ? COMPAT_UNKNOWN,

? ? ? COMPAT_NVIDIA_TEGRA20_USB, ? ? ?/* Tegra2 USB port */
+ ? ? COMPAT_NVIDIA_TEGRA20_I2C, ? ? ?/* Tegra2 i2c */

? ? ? COMPAT_COUNT,
?};

lib/fdtdec.c +1 -0
index 931b4ce..fb3d79d 100644
@@ -38,6 +38,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_TEGRA20_I2C, "nvidia,tegra20-i2c"),
 ?};
 
Quoted 3 lines
Quoted 1 line

?/**

M

Regards,
Simon

#19 lore raw

PATCH 4/7 tegra: Add I2C driver

In reply to Simon Glass

drivers/i2c/tegra2_i2c.c line 142

Quoted 8 lines · On Monday 26 December 2011 13:11:48 Simon Glass wrote:
drivers/i2c/tegra2_i2c.c new file +3 -0
 
+static int wait_for_tx_fifo_empty(struct i2c_control *control)
...
+	while (timeout_us >= 0) {
...
+	};

no semicolon here

drivers/i2c/tegra2_i2c.c line 159

Quoted 5 lines
drivers/i2c/tegra2_i2c.c new file +3 -0
+static int wait_for_rx_fifo_notempty(struct i2c_control *control)
...
+	while (timeout_us >= 0) {
...
+	};

no semicolon here

drivers/i2c/tegra2_i2c.c line 180

Quoted 5 lines
drivers/i2c/tegra2_i2c.c new file +3 -0
+static int wait_for_transfer_complete(struct i2c_control *control)
...
+	while (timeout_us >= 0) {
...
+	};

no semicolon here
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20120108/93a22cf9/attachment.pgp>

#20 lore raw

PATCH 4/7 tegra: Add I2C driver

Hi Mike,

Quoted 20 lines · Mike Frysinger

drivers/i2c/tegra2_i2c.c

Quoted 8 lines · On Monday 26 December 2011 13:11:48 Simon Glass wrote:
drivers/i2c/tegra2_i2c.c new file +3 -0
 
+static int wait_for_tx_fifo_empty(struct i2c_control *control)
...
+ ? ? while (timeout_us >= 0) {
...
+ ? ? };

no semicolon here

Quoted 5 lines

+static int wait_for_rx_fifo_notempty(struct i2c_control *control)
...
+ ? ? while (timeout_us >= 0) {
...
+ ? ? };

no semicolon here

Quoted 8 lines
Quoted 5 lines

+static int wait_for_transfer_complete(struct i2c_control *control)
...
+ ? ? while (timeout_us >= 0) {
...
+ ? ? };

no semicolon here
-mike

done - thanks.

Regards,
Simon

#21 lore raw

PATCH 4/7 tegra: Add I2C driver

In reply to Simon Glass
Quoted 12 lines · Simon Glass

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

...

Quoted 1 line
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h +0 -0

...

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 40

Quoted 5 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h +5 -0
+#ifndef CONFIG_OF_CONTROL
+enum {
+       I2CSPEED_KHZ = 100,             /* in KHz */
+};
+#endif

The patch description says the board needs to define I2CSPEED_KHZ, yet that seems to allow the board to override it, otherwise a default is used. The patch description probably needs to be fixed.

The way the optional override is implemented is a little confusing. I'd rather see:

#ifndef CONFIG_OF_CONTROL
#ifndef I2CSPEED_KHZ
#define I2CSPEED_KHZ 100
#endif
#endif

That makes it a lot more obvious it's a default. And actually, why not put that within some other ifndef CONFIG_OF_CONTROL where the enum/define is used, since it's presumably only used very locally in the non-OF initialization code.

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

I didn't really review the I2C driver code itself, since I'm not terribly familiar with the I2C HW details.

drivers/i2c/tegra2_i2c.c line 341

Quoted 9 lines
drivers/i2c/tegra2_i2c.c +9 -0
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+       const void *blob = gd->fdt_blob;
+       int node;
+
+       node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
+                                index);
+       if (node < 0)
+               return -1;

I assume the I2C patches will also be reworked to enumerate based on nodes with a specific compatible flag, and then optionally using aliases to number them, just like you said you're rework USB?

Quoted 11 lines
drivers/i2c/tegra2_i2c.c +11 -0
+int i2c_init_board(void)
+{
+       struct i2c_bus *i2c_bus;
+       int index = 0;
+       int i;
+
+       /* build the i2c_controllers[] for each controller */
+       for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+               i2c_bus = &i2c_controllers[i];
+               i2c_bus->id = i;
+

drivers/i2c/tegra2_i2c.c line 375

Quoted 10 lines
drivers/i2c/tegra2_i2c.c +10 -0
+               if (i2c_get_config(&index, i2c_bus)) {
+                       printf("i2c_init_board: failed to find bus %d\n", i);
+                       return -1;
+               }
+
+               if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+                       i2c_bus->control =
+                               &((struct dvc_ctlr *)i2c_bus->regs)->control;
+               else
+                       i2c_bus->control = &i2c_bus->regs->control;

When instantiating controllers from device tree (as opposed to the static !CONFIG_OF_CONTROL case), that conditional should be driven by device tree properties. The kernel already represents this using two separate compatible values for the different HW: nvidia,tegra20-i2c and nvidia,tegra20-i2c-dvc.

drivers/i2c/tegra2_i2c.c line 386

Quoted 11 lines
drivers/i2c/tegra2_i2c.c +10 -0
+
+               i2c_init_controller(i2c_bus);
+       }
+
+       return 0;
+}
 
+void i2c_init(int speed, int slaveaddr)
+{
+       debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

Quoted 18 lines
drivers/i2c/tegra2_i2c.c +17 -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, &reg, 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)

drivers/i2c/tegra2_i2c.c line 454

Quoted 10 lines
drivers/i2c/tegra2_i2c.c +10 -0
+{
+       if (alen < 0 || alen > sizeof(addr))
+               return 0;
+       if (alen != sizeof(addr)) {
+               uint max_addr = (1 << (8 * alen)) - 1;
+               if (addr > max_addr)
+                       return 0;
+       }
+       return 1;
+}

That seems rather roundabout. Only two address lengths are valid; 7 and 10-bit, so wouldn't it be better to only accept those specific values?

Signature · 1 line
nvpublic
#22 lore raw

PATCH 4/7 tegra: Add I2C driver

Hi Stephem,

Quoted 35 lines · Stephen Warren
Quoted 12 lines · Simon Glass

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3) ? ? ? ? (typically this will be 0 to bring the port out the common ? ? ? ? ? ? ? ? pins)

...

Quoted 1 line
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h +0 -0

...

arch/arm/include/asm/arch-tegra2/tegra2_i2c.h line 40

Quoted 5 lines
arch/arm/include/asm/arch-tegra2/tegra2_i2c.h +5 -0
+#ifndef CONFIG_OF_CONTROL
+enum {
+ ? ? ? I2CSPEED_KHZ = 100, ? ? ? ? ? ? /* in KHz */
+};
+#endif

The patch description says the board needs to define I2CSPEED_KHZ, yet that seems to allow the board to override it, otherwise a default is used. The patch description probably needs to be fixed.

The way the optional override is implemented is a little confusing. I'd rather see:

#ifndef CONFIG_OF_CONTROL
#ifndef I2CSPEED_KHZ
#define I2CSPEED_KHZ 100
#endif
#endif

Quoted 4 lines

That makes it a lot more obvious it's a default. And actually, why not put that within some other ifndef CONFIG_OF_CONTROL where the enum/define is used, since it's presumably only used very locally in the non-OF initialization code.

I'm removing the non-OF code since we don't use it.

Quoted 9 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

Quoted 3 lines

I didn't really review the I2C driver code itself, since I'm not terribly familiar with the I2C HW details.

It had quite a bit of review before it went into our tree, and it certainly has had a lot of testing.

Quoted 14 lines

drivers/i2c/tegra2_i2c.c

Quoted 10 lines
drivers/i2c/tegra2_i2c.c +9 -0
+static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
+{
+ ? ? ? const void *blob = gd->fdt_blob;
+ ? ? ? int node;
+
+ ? ? ? node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?index);
+ ? ? ? if (node < 0)
+ ? ? ? ? ? ? ? return -1;

I assume the I2C patches will also be reworked to enumerate based on nodes with a specific compatible flag, and then optionally using aliases to number them, just like you said you're rework USB?

Yes, will do. The hard bit of that was creating an fdtdec function to handle it.

Quoted 20 lines
Quoted 20 lines
drivers/i2c/tegra2_i2c.c +19 -0
+int i2c_init_board(void)
+{
+ ? ? ? struct i2c_bus *i2c_bus;
+ ? ? ? int index = 0;
+ ? ? ? int i;
+
+ ? ? ? /* build the i2c_controllers[] for each controller */
+ ? ? ? for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+ ? ? ? ? ? ? ? i2c_bus = &i2c_controllers[i];
+ ? ? ? ? ? ? ? i2c_bus->id = i;
+
+ ? ? ? ? ? ? ? if (i2c_get_config(&index, i2c_bus)) {
+ ? ? ? ? ? ? ? ? ? ? ? printf("i2c_init_board: failed to find bus %d\n", i);
+ ? ? ? ? ? ? ? ? ? ? ? return -1;
+ ? ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? ? if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control =
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &((struct dvc_ctlr *)i2c_bus->regs)->control;
Quoted 8 lines
Quoted 2 lines

+ ? ? ? ? ? ? ? else
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control = &i2c_bus->regs->control;

When instantiating controllers from device tree (as opposed to the static !CONFIG_OF_CONTROL case), that conditional should be driven by device tree properties. The kernel already represents this using two separate compatible values for the different HW: nvidia,tegra20-i2c and nvidia,tegra20-i2c-dvc.

Not in the device tree file I got from the kernel...has it changed?

I will make this change. By dropping non-fdt use it will make this work almost for free.

Quoted 8 lines
Quoted 7 lines
drivers/i2c/tegra2_i2c.c +6 -0
+
+ ? ? ? ? ? ? ? i2c_init_controller(i2c_bus);
+ ? ? ? }
+
+ ? ? ? return 0;
+}
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 386

Quoted 4 lines
drivers/i2c/tegra2_i2c.c +4 -0
+void i2c_init(int speed, int slaveaddr)
+{
+ ? ? ? debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

If fdt use grows then perhaps U-Boot might accept patches to understand the concept of initing i2c as a unit, and once at the start (or first use), rather than the current setup.

Quoted 18 lines
Quoted 17 lines
drivers/i2c/tegra2_i2c.c +16 -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, &reg, 1);
+ ? ? ? if (rc) {
+ ? ? ? ? ? ? ? debug("Error probing 0x%x.\n", chip);
+ ? ? ? ? ? ? ? return 1;
+ ? ? ? }
+ ? ? ? return 0;
+}
+
Quoted 14 lines

drivers/i2c/tegra2_i2c.c

Quoted 11 lines
drivers/i2c/tegra2_i2c.c +11 -0
+static int i2c_addr_ok(const uint addr, const int alen)
+{
+ ? ? ? if (alen < 0 || alen > sizeof(addr))
+ ? ? ? ? ? ? ? return 0;
+ ? ? ? if (alen != sizeof(addr)) {
+ ? ? ? ? ? ? ? uint max_addr = (1 << (8 * alen)) - 1;
+ ? ? ? ? ? ? ? if (addr > max_addr)
+ ? ? ? ? ? ? ? ? ? ? ? return 0;
+ ? ? ? }
+ ? ? ? return 1;
+}

That seems rather roundabout. Only two address lengths are valid; 7 and 10-bit, so wouldn't it be better to only accept those specific values?

Yes I agree - I will change it to just check that one or two address bytes are provided.

Regards,
Simon

Quoted 3 lines

--
nvpublic

#23 lore raw

PATCH 4/7 tegra: Add I2C driver

Simon Glass wrote ednesday, January 11, 2012 9:18 PM:

Quoted 14 lines · Stephen Warren
Quoted 13 lines · Simon Glass
Quoted 12 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3) ? ? ? ? (typically this will be 0 to bring the port out the common ? ? ? ? ? ? ? ? pins)

...

Quoted 11 lines
Quoted 8 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

We may not support it now, but I see no reason we won't in the future. If we confuse the two defines now, it'll make it harder to allow muxes in the future. The fix is simply using the correct define name within the I2C driver isn't it; pretty simple.

Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
drivers/i2c/tegra2_i2c.c +19 -0
+int i2c_init_board(void)
+{
+ ? ? ? struct i2c_bus *i2c_bus;
+ ? ? ? int index = 0;
+ ? ? ? int i;
+
+ ? ? ? /* build the i2c_controllers[] for each controller */
+ ? ? ? for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+ ? ? ? ? ? ? ? i2c_bus = &i2c_controllers[i];
+ ? ? ? ? ? ? ? i2c_bus->id = i;
+
+ ? ? ? ? ? ? ? if (i2c_get_config(&index, i2c_bus)) {
+ ? ? ? ? ? ? ? ? ? ? ? printf("i2c_init_board: failed to find bus %d\n", i);
+ ? ? ? ? ? ? ? ? ? ? ? return -1;
+ ? ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? ? if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control =
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &((struct dvc_ctlr *)i2c_bus->regs)->control;
Quoted 8 lines
Quoted 2 lines

+ ? ? ? ? ? ? ? else
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control = &i2c_bus->regs->control;

When instantiating controllers from device tree (as opposed to the static !CONFIG_OF_CONTROL case), that conditional should be driven by device tree properties. The kernel already represents this using two separate compatible values for the different HW: nvidia,tegra20-i2c and nvidia,tegra20-i2c-dvc.

Quoted 1 line

Not in the device tree file I got from the kernel...has it changed?

Yes, the latest is:

        i2c at 7000c000 {
                compatible = "nvidia,tegra20-i2c";
        i2c at 7000c400 {
                compatible = "nvidia,tegra20-i2c";
        i2c at 7000c500 {
                compatible = "nvidia,tegra20-i2c";
        i2c at 7000d000 {
                compatible = "nvidia,tegra20-i2c-dvc";
Quoted 16 lines
Quoted 7 lines
Quoted 6 lines
drivers/i2c/tegra2_i2c.c +6 -0
+
+ ? ? ? ? ? ? ? i2c_init_controller(i2c_bus);
+ ? ? ? }
+
+ ? ? ? return 0;
+}
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 386

Quoted 4 lines
drivers/i2c/tegra2_i2c.c +4 -0
+void i2c_init(int speed, int slaveaddr)
+{
+ ? ? ? debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

Quoted 3 lines

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

Hmm. I suspect the answer here is roughly to override the following in cmd_i2c.c:

/* TODO: Implement architecture-specific get/set functions */ unsigned int __def_i2c_get_bus_speed(void)

{
        return CONFIG_SYS_I2C_SPEED;
}
unsigned int i2c_get_bus_speed(void)
        __attribute__((weak, alias("__def_i2c_get_bus_speed")));

int __def_i2c_set_bus_speed(unsigned int speed)

{
        if (speed != CONFIG_SYS_I2C_SPEED)
                return -1;

        return 0;
}
int i2c_set_bus_speed(unsigned int)
        __attribute__((weak, alias("__def_i2c_set_bus_speed")));

To actually read/write the rate in use by the driver.

Then, fix do_i2c_reset() to use i2c_get_bus_speed() so it interacts correctly with those functions.

There may be other places that need to be updates to use those function instead of hard-coding CONFIG_SYS_I2C_SPEED too. Perhaps we could even get away without defining CONFIG_SYS_I2C_SPEED for Tegra since it's meaningless. Instead, ifdef those default function definitions above based on whether CONFIG_SYS_I2C_SPEED is defined or not.

Signature · 1 line
nvpublic
#24 lore raw

PATCH 4/7 tegra: Add I2C driver

Hello Stephen,

Quoted 24 lines · Stephen Warren

Simon Glass wrote ednesday, January 11, 2012 9:18 PM:

Quoted 14 lines · Stephen Warren
Quoted 13 lines · Simon Glass
Quoted 12 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

...

Quoted 7 lines
Quoted 7 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

Quoted 7 lines
Quoted 2 lines

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

We may not support it now, but I see no reason we won't in the future. If we confuse the two defines now, it'll make it harder to allow muxes in the future. The fix is simply using the correct define name within the I2C driver isn't it; pretty simple.

If we really have this case, we *must* get the multibus/multiadapter branch from here:

http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/multibus_v2_20111112

to uboot mainline! With that approach we could handle this case in a clean way ... This branch is not in sync with current TOT, and it has to be tested again, as I didn't found time for this in the last year :-( Also, as its base is >2 years old, there are a lot of checkpatch errors in this patchserie, which have to be cleaned up ... hope I get some time for it ... help is welcome.

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
drivers/i2c/tegra2_i2c.c +2 -0
+int i2c_init_board(void)
+{

[...]

Quoted 28 lines
Quoted 6 lines
Quoted 3 lines

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

Hmm. I suspect the answer here is roughly to override the following in cmd_i2c.c:

/* TODO: Implement architecture-specific get/set functions */ unsigned int __def_i2c_get_bus_speed(void)

{
        return CONFIG_SYS_I2C_SPEED;
}
unsigned int i2c_get_bus_speed(void)
        __attribute__((weak, alias("__def_i2c_get_bus_speed")));

int __def_i2c_set_bus_speed(unsigned int speed)

{
        if (speed != CONFIG_SYS_I2C_SPEED)
                return -1;

        return 0;
}
int i2c_set_bus_speed(unsigned int)
        __attribute__((weak, alias("__def_i2c_set_bus_speed")));
Quoted 1 line

To actually read/write the rate in use by the driver.

Yep, for this reason this functions are weak, and you can define a driver specific function which returns the current used settings in the driver.

Code should compile, if tegra2 not define CONFIG_SYS_I2C_SPEED, as a "default" value for CONFIG_SYS_I2C_SPEED is defined in include/i2c.h ...

Quoted 2 lines

Then, fix do_i2c_reset() to use i2c_get_bus_speed() so it interacts correctly with those functions.

Yep, I think, you are right here, that should be fixed. As the default function for i2c_get_bus_speed returns CONFIG_SYS_I2C_SPEED, that should be OK.

Quoted 6 lines

There may be other places that need to be updates to use those function instead of hard-coding CONFIG_SYS_I2C_SPEED too. Perhaps we could even get away without defining CONFIG_SYS_I2C_SPEED for Tegra since it's meaningless. Instead, ifdef those default function definitions above based on whether CONFIG_SYS_I2C_SPEED is defined or not.

bye,
Heiko

Signature · 2 lines
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#25 lore raw

PATCH 4/7 tegra: Add I2C driver

Hi Heiko,

Quoted 38 lines · Heiko Schocher

Hello Stephen,

Quoted 24 lines · Stephen Warren

Simon Glass wrote ednesday, January 11, 2012 9:18 PM:

Quoted 14 lines · Stephen Warren
Quoted 13 lines · Simon Glass
Quoted 12 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3) ? ? ? ? (typically this will be 0 to bring the port out the common ? ? ? ? ? ? ? ? pins)

...

Quoted 7 lines
Quoted 7 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

Quoted 7 lines
Quoted 2 lines

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

We may not support it now, but I see no reason we won't in the future. If we confuse the two defines now, it'll make it harder to allow muxes in the future. The fix is simply using the correct define name within the I2C driver isn't it; pretty simple.

If we really have this case, we *must* get the multibus/multiadapter branch from here:

Quoted 9 lines

http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/multibus_v2_20111112

to uboot mainline! With that approach we could handle this case in a clean way ... This branch is not in sync with current TOT, and it has to be tested again, as I didn't found time for this in the last year :-( Also, as its base is >2 years old, there are a lot of checkpatch errors in this patchserie, which have to be cleaned up ... hope I get some time for it ... help is welcome.

Hmm quite a few patches. I see this email from 2.5 years ago indicating that it was ready but needed more testing:

http://lists.denx.de/pipermail/u-boot/2009-July/056726.html

2a083b7 i2c: get rid of HARD_i2C
ccb680c i2c: add dynamic bus allocation
b68480a i2c, u8500: added new multibus/multiadapter support 692f037 i2c, marvell: added new multibus/multiadapter support 3b0258c i2c, spear: added new multibus/multiadapter support a06deff i2c, at91rm9200: added new multibus/multiadapter support 0758f6b i2c, kirkwood: added new multibus/multiadapter support 7919863 i2c, mv: added new multibus/multiadapter support b164243 i2c, mpc824x: added new multibus/multiadapter support a5f8bd1 i2c, tsi108: added new multibus/multiadapter support 9db40d8 i2c, s3c44b0: added new multibus/multiadapter support 67b95ef i2c, s3c24x0: added new multibus/multiadapter support 6b2c3f5 i2c, omap24xx: added new multibus/multiadapter support 1f8ffed i2c, omap1510: added new multibus/multiadapter support c41757c i2c, mxc: added new multibus/multiadapter support 5c22159 i2c, davinci: added new multibus/multiadapter support 3dd28ab i2c, bfin: added new multibus/multiadapter support 3d1c119 i2c, mpc8220: added new multibus/multiadapter support 8f4d062 i2c, mpc512x: added new multibus/multiadapter support 1bd40d4 i2c, mpc5xxx: added new multibus/multiadapter support 6225ccb i2c, 8xx: added new multibus/multiadapter support d94a632 i2c, ppc4xx_i2c: added new multibus/multiadapter support c34a705 i2c, mpc8260_i2c: added new multibus/multiadapter support 832b409 i2c, fsl_i2c: switch to new multibus/multiadapter support 06de378 i2c, soft-i2c: switch to new multibus/multiadapter support e47115f i2c, common: common changes for multibus/multiadapter support d7a70d7 i2c: add i2c_core and prepare for new multibus support

 489 files changed, 8512 insertions(+), 7063 deletions(-)

checkpatch.pl found 295 error(s), 653 warning(s)

I have had a look through the patches. I agree that i2c needs a clean up and this seems to fit the bill. How can we get this committed?

The first two commits aren't too hard to bring over but it doesn't actually build. I could perhaps have a look at getting it to build correctly with and without CONFIG_SYS_I2C, and see if I can get the latter working on Tegra.

Then perhaps we could add the new support as just another option so we have CONFIG_HARD_I2C, CONFIG_SOFT_I2C and CONFIG_SYS_I2C. That might be a start, and drivers can move over to the new system. What do you think?

Quoted 41 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
drivers/i2c/tegra2_i2c.c +2 -0
+int i2c_init_board(void)
+{

[...]

Quoted 23 lines
Quoted 6 lines
Quoted 3 lines

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

Hmm. I suspect the answer here is roughly to override the following in cmd_i2c.c:

/* TODO: Implement architecture-specific get/set functions */ unsigned int __def_i2c_get_bus_speed(void)
{
? ? ? ? return CONFIG_SYS_I2C_SPEED;
}
unsigned int i2c_get_bus_speed(void)
? ? ? ? __attribute__((weak, alias("__def_i2c_get_bus_speed")));

int __def_i2c_set_bus_speed(unsigned int speed)
{
? ? ? ? if (speed != CONFIG_SYS_I2C_SPEED)
? ? ? ? ? ? ? ? return -1;

Quoted 6 lines

? ? ? ? return 0;
}
int i2c_set_bus_speed(unsigned int)
? ? ? ? __attribute__((weak, alias("__def_i2c_set_bus_speed")));

To actually read/write the rate in use by the driver.

Yep, for this reason this functions are weak, and you can define a driver specific function which returns the current used settings in the driver.

Code should compile, if tegra2 not define CONFIG_SYS_I2C_SPEED, as a "default" value for CONFIG_SYS_I2C_SPEED is defined in include/i2c.h ...

Quoted 6 lines
Quoted 2 lines

Then, fix do_i2c_reset() to use i2c_get_bus_speed() so it interacts correctly with those functions.

Yep, I think, you are right here, that should be fixed. As the default function for i2c_get_bus_speed returns CONFIG_SYS_I2C_SPEED, that should be OK.

Yes ok I will take a look.

Regards,
Simon

Quoted 13 lines
Quoted 7 lines

There may be other places that need to be updates to use those function instead of hard-coding CONFIG_SYS_I2C_SPEED too. Perhaps we could even get away without defining CONFIG_SYS_I2C_SPEED for Tegra since it's meaningless. Instead, ifdef those default function definitions above based on whether CONFIG_SYS_I2C_SPEED is defined or not.

bye,
Heiko
--
DENX Software Engineering GmbH, ? ? MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

#26 lore raw

PATCH 4/7 tegra: Add I2C driver

Hello Simon,

Quoted 49 lines · Simon Glass

Hi Heiko,

Quoted 36 lines · Heiko Schocher

Hello Stephen,

Quoted 24 lines · Stephen Warren

Simon Glass wrote ednesday, January 11, 2012 9:18 PM:

Quoted 14 lines · Stephen Warren
Quoted 13 lines · Simon Glass
Quoted 12 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
(typically this will be 0 to bring the port out the common
pins)

...

Quoted 7 lines
Quoted 7 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

Quoted 6 lines
Quoted 2 lines

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

We may not support it now, but I see no reason we won't in the future. If we confuse the two defines now, it'll make it harder to allow muxes in the future. The fix is simply using the correct define name within the I2C driver isn't it; pretty simple.

If we really have this case, we *must* get the multibus/multiadapter branch from here:

Quoted 9 lines

http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/multibus_v2_20111112

to uboot mainline! With that approach we could handle this case in a clean way ... This branch is not in sync with current TOT, and it has to be tested again, as I didn't found time for this in the last year :-( Also, as its base is >2 years old, there are a lot of checkpatch errors in this patchserie, which have to be cleaned up ... hope I get some time for it ... help is welcome.

Quoted 1 line

Hmm quite a few patches. I see this email from 2.5 years ago

Yep, there was some work on this ... but the last year, I just found time for rebasing this branch, add new boards/i2c drivers and just try compile clean ... so there are maybe some errors, which you can solve only with a debugger ... Sorry for that ...

Quoted 32 lines

indicating that it was ready but needed more testing:

http://lists.denx.de/pipermail/u-boot/2009-July/056726.html

2a083b7 i2c: get rid of HARD_i2C
ccb680c i2c: add dynamic bus allocation
b68480a i2c, u8500: added new multibus/multiadapter support 692f037 i2c, marvell: added new multibus/multiadapter support 3b0258c i2c, spear: added new multibus/multiadapter support a06deff i2c, at91rm9200: added new multibus/multiadapter support 0758f6b i2c, kirkwood: added new multibus/multiadapter support 7919863 i2c, mv: added new multibus/multiadapter support b164243 i2c, mpc824x: added new multibus/multiadapter support a5f8bd1 i2c, tsi108: added new multibus/multiadapter support 9db40d8 i2c, s3c44b0: added new multibus/multiadapter support 67b95ef i2c, s3c24x0: added new multibus/multiadapter support 6b2c3f5 i2c, omap24xx: added new multibus/multiadapter support 1f8ffed i2c, omap1510: added new multibus/multiadapter support c41757c i2c, mxc: added new multibus/multiadapter support 5c22159 i2c, davinci: added new multibus/multiadapter support 3dd28ab i2c, bfin: added new multibus/multiadapter support 3d1c119 i2c, mpc8220: added new multibus/multiadapter support 8f4d062 i2c, mpc512x: added new multibus/multiadapter support 1bd40d4 i2c, mpc5xxx: added new multibus/multiadapter support 6225ccb i2c, 8xx: added new multibus/multiadapter support d94a632 i2c, ppc4xx_i2c: added new multibus/multiadapter support c34a705 i2c, mpc8260_i2c: added new multibus/multiadapter support 832b409 i2c, fsl_i2c: switch to new multibus/multiadapter support 06de378 i2c, soft-i2c: switch to new multibus/multiadapter support e47115f i2c, common: common changes for multibus/multiadapter support d7a70d7 i2c: add i2c_core and prepare for new multibus support

Quoted 3 lines
 489 files changed, 8512 insertions(+), 7063 deletions(-)

checkpatch.pl found 295 error(s), 653 warning(s)

Yep, I know :-(

Quoted 2 lines

I have had a look through the patches. I agree that i2c needs a clean up and this seems to fit the bill. How can we get this committed?

We should try to get rid of the checkpatch errors. After that we should test it on some platforms (arm, powerpc) and if this step is done, we can bring it to mainline in the next upcoming merge window.

If we break i2c support on some boards, I hope board maintainers will speak up then, and send bugfix patches ;-)

Quoted 2 lines

The first two commits aren't too hard to bring over but it doesn't actually build. I could perhaps have a look at getting it to build

Hups, I run a "MAKEALL -a arm" without errors ... will look into this next week.

Quoted 7 lines

correctly with and without CONFIG_SYS_I2C, and see if I can get the latter working on Tegra.

Then perhaps we could add the new support as just another option so we have CONFIG_HARD_I2C, CONFIG_SOFT_I2C and CONFIG_SYS_I2C. That might be a start, and drivers can move over to the new system. What do you think?

I would prefer to get the hole branch to mainline, so we can get rid off the CONFIG_HARD_I2C define ... but maybe it is worth to try this in more smaller steps ...

Quoted 39 lines
Quoted 39 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
drivers/i2c/tegra2_i2c.c +2 -0
+int i2c_init_board(void)
+{

[...]

Quoted 27 lines
Quoted 6 lines
Quoted 3 lines

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

Hmm. I suspect the answer here is roughly to override the following in cmd_i2c.c:

/* TODO: Implement architecture-specific get/set functions */ unsigned int __def_i2c_get_bus_speed(void)

{
        return CONFIG_SYS_I2C_SPEED;
}
unsigned int i2c_get_bus_speed(void)
        __attribute__((weak, alias("__def_i2c_get_bus_speed")));

int __def_i2c_set_bus_speed(unsigned int speed)

{
        if (speed != CONFIG_SYS_I2C_SPEED)
                return -1;

        return 0;
}
int i2c_set_bus_speed(unsigned int)
        __attribute__((weak, alias("__def_i2c_set_bus_speed")));
Quoted 1 line

To actually read/write the rate in use by the driver.

Yep, for this reason this functions are weak, and you can define a driver specific function which returns the current used settings in the driver.

Code should compile, if tegra2 not define CONFIG_SYS_I2C_SPEED, as a "default" value for CONFIG_SYS_I2C_SPEED is defined in include/i2c.h ...

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Then, fix do_i2c_reset() to use i2c_get_bus_speed() so it interacts correctly with those functions.

Yep, I think, you are right here, that should be fixed. As the default function for i2c_get_bus_speed returns CONFIG_SYS_I2C_SPEED, that should be OK.

Yes ok I will take a look.

Thanks!

bye,
Heiko

Signature · 2 lines
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#27 lore raw

PATCH 4/7 tegra: Add I2C driver

In reply to Stephen Warren

Hi Stephen,

Quoted 26 lines · Stephen Warren

Simon Glass wrote ednesday, January 11, 2012 9:18 PM:

Quoted 14 lines · Stephen Warren
Quoted 13 lines · Simon Glass
Quoted 12 lines

From: Yen Lin <[email protected]>

Add basic i2c driver for Tegra2 with 8- and 16-bit address support. The driver supports building both with and without CONFIG_OF_CONTROL.

Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied in the board config header file:

I2CSPEED_KHZ - speed to run I2C bus at (typically 100000) CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3) ? ? ? ? (typically this will be 0 to bring the port out the common ? ? ? ? ? ? ? ? pins)

...

Quoted 9 lines
Quoted 8 lines

...

Quoted 1 line
drivers/i2c/tegra2_i2c.c +0 -0

...

Quoted 1 line

+struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];

What if there are I2C bus extenders/muxes/... such that there are more I2C buses in the system than Tegra I2C controllers? I'd rather see an explicit TEGRA_I2C_NUM_CONTROLLERS define used throughout this patch.

Quoted 7 lines
Quoted 2 lines

We don't actually support CONFIG_I2C_MUX, so I can't see how that could happen. Can you please explain a bit more?

We may not support it now, but I see no reason we won't in the future. If we confuse the two defines now, it'll make it harder to allow muxes in the future. The fix is simply using the correct define name within the I2C driver isn't it; pretty simple.

OK I have added the new define.

Quoted 33 lines
Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
drivers/i2c/tegra2_i2c.c +19 -0
+int i2c_init_board(void)
+{
+ ? ? ? struct i2c_bus *i2c_bus;
+ ? ? ? int index = 0;
+ ? ? ? int i;
+
+ ? ? ? /* build the i2c_controllers[] for each controller */
+ ? ? ? for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
+ ? ? ? ? ? ? ? i2c_bus = &i2c_controllers[i];
+ ? ? ? ? ? ? ? i2c_bus->id = i;
+
+ ? ? ? ? ? ? ? if (i2c_get_config(&index, i2c_bus)) {
+ ? ? ? ? ? ? ? ? ? ? ? printf("i2c_init_board: failed to find bus %d\n", i);
+ ? ? ? ? ? ? ? ? ? ? ? return -1;
+ ? ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? ? if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control =
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &((struct dvc_ctlr *)i2c_bus->regs)->control;
Quoted 8 lines
Quoted 2 lines

+ ? ? ? ? ? ? ? else
+ ? ? ? ? ? ? ? ? ? ? ? i2c_bus->control = &i2c_bus->regs->control;

When instantiating controllers from device tree (as opposed to the static !CONFIG_OF_CONTROL case), that conditional should be driven by device tree properties. The kernel already represents this using two separate compatible values for the different HW: nvidia,tegra20-i2c and nvidia,tegra20-i2c-dvc.

Quoted 1 line

Not in the device tree file I got from the kernel...has it changed?

Yes, the latest is:

Quoted 8 lines

? ? ? ?i2c at 7000c000 {
? ? ? ? ? ? ? ?compatible = "nvidia,tegra20-i2c"; ? ? ? ?i2c at 7000c400 {
? ? ? ? ? ? ? ?compatible = "nvidia,tegra20-i2c"; ? ? ? ?i2c at 7000c500 {
? ? ? ? ? ? ? ?compatible = "nvidia,tegra20-i2c"; ? ? ? ?i2c at 7000d000 {
? ? ? ? ? ? ? ?compatible = "nvidia,tegra20-i2c-dvc";

OK I think I have this now.

Quoted 43 lines
Quoted 18 lines
Quoted 9 lines
Quoted 8 lines
drivers/i2c/tegra2_i2c.c +6 -0
+
+ ? ? ? ? ? ? ? i2c_init_controller(i2c_bus);
+ ? ? ? }
+
+ ? ? ? return 0;
+}
Quoted 8 lines

drivers/i2c/tegra2_i2c.c line 386

Quoted 4 lines
drivers/i2c/tegra2_i2c.c +4 -0
+void i2c_init(int speed, int slaveaddr)
+{
+ ? ? ? debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+}

Surely that function needs to actually do something, at least set up the clocks so that the (user?) requested rate is honored, or print an error message if you're only allowed to use the hard-coded bus rate.

Quoted 3 lines

See my other message. I suppose we could reinit, but we really don't want to honour the speed, since the fdt speed setting is then lost and irrecoverable. For now it feels like we should ignore it.

Hmm. I suspect the answer here is roughly to override the following in cmd_i2c.c:

/* TODO: Implement architecture-specific get/set functions */ unsigned int __def_i2c_get_bus_speed(void)
{
? ? ? ?return CONFIG_SYS_I2C_SPEED;
}
unsigned int i2c_get_bus_speed(void)
? ? ? ?__attribute__((weak, alias("__def_i2c_get_bus_speed")));

int __def_i2c_set_bus_speed(unsigned int speed)
{
? ? ? ?if (speed != CONFIG_SYS_I2C_SPEED)
? ? ? ? ? ? ? ?return -1;

? ? ? ?return 0;
}
int i2c_set_bus_speed(unsigned int)
? ? ? ?__attribute__((weak, alias("__def_i2c_set_bus_speed")));

Quoted 4 lines

To actually read/write the rate in use by the driver.

Then, fix do_i2c_reset() to use i2c_get_bus_speed() so it interacts correctly with those functions.

The reset will override the fdt speed, but there's no easy way around that. U-Boot expects a single speed for all I2C at present.

Quoted 6 lines

There may be other places that need to be updates to use those function instead of hard-coding CONFIG_SYS_I2C_SPEED too. Perhaps we could even get away without defining CONFIG_SYS_I2C_SPEED for Tegra since it's meaningless. Instead, ifdef those default function definitions above based on whether CONFIG_SYS_I2C_SPEED is defined or not.

I think we should leave this until the I2C refactor is in. In fact I would go further and say that U-Boot should probably support more flexibility here - but the API is what it is at the moment and I feel uncomfortable changing it just for Tegra. As you probably saw I sent up Heiko's patches and one or other of us may get to this.

Regards,
Simon

Quoted 4 lines

--
nvpublic

#28 lore raw

PATCH 5/7 tegra: Initialise I2C on Nvidia boards

In reply to Simon Glass

This enables I2C on all Nvidia boards including Seaboard and Harmony.

---
 board/nvidia/common/board.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
board/nvidia/common/board.c +4 -0
index a7c566d..63edfdb 100644
@@ -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;
@@ -69,6 +70,9 @@ int board_init(void)
 #endif
 	/* boot param addr */
 	gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
+#ifdef CONFIG_TEGRA2_I2C
+	i2c_init_board();
+#endif
 
 #ifdef CONFIG_USB_EHCI_TEGRA
 	/* For USB GPIO PD0. for now, since we have no pinmux in fdt */
Signature · 1 line
1.7.3.1
#29 lore raw

PATCH 6/7 tegra: Select I2C ordering for Seaboard

In reply to Simon Glass

Select the port ordering for I2C on Seaboard.

---
 board/nvidia/dts/tegra2-seaboard.dts |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
board/nvidia/dts/tegra2-seaboard.dts +5 -0
index 839e761..187dec0 100644
@@ -15,6 +15,11 @@
 		/* This defines the order of our USB ports */
 		usb0 = "/usb at c5008000";
 		usb1 = "/usb at c5000000";
+
+		i2c0 = "/i2c at 7000d000";
+		i2c1 = "/i2c at 7000c000";
+		i2c2 = "/i2c at 7000c400";
+		i2c3 = "/i2c at 7000c500";
 	};
 
 	memory {
Signature · 1 line
1.7.3.1
#31 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

In reply to Simon Glass

This enables I2C on Seaboard.

---
 include/configs/seaboard.h |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)
include/configs/seaboard.h +8 -0
index 1dc775a..6e04278 100644
@@ -59,6 +59,14 @@
 #define CONFIG_CMD_SF
 #define CONFIG_SPI_FLASH_SIZE		(4 << 20)
 
+/* I2C */
+#define CONFIG_TEGRA2_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
Signature · 1 line
1.7.3.1
#32 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

Quoted 1 line · Simon Glass

This enables I2C on Seaboard.

...

Quoted 1 line
include/configs/seaboard.h +0 -0

...

Quoted 1 line

+#define CONFIG_SYS_I2C_INIT_BOARD

I don't think that option is correct for Seaboard; the description in the README indicates this causes a function named i2c_init_board() to be called from boards/xxx/board.c, which is supposed to use GPIOs to unhang the I2C bus. However, this raises a couple of issues:

1) Patch 5 in this series calls i2c_init_board() ifdef CONFIG_TEGRA2_I2C rather than depending on this CONFIG option.

2) Tegra's i2c_init_board() doesn't appear to be anything to do with bus unhanging, but is instead regular I2C initialization. Perhaps the function should be renamed?

Signature · 1 line
nvpublic

A message that is not in the archive (sent off-list or not yet received): CAPnjgZ3jTm6j-fK_Kn==W3uGr=8pREEWXawP39kojLzzSH07wQ@mail.gmail.com

A message that is not in the archive (sent off-list or not yet received): [email protected]

#35 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

+U-Boot, which I seemed to have dropped by mistake

Hi Stephen,

Quoted 28 lines · Stephen Warren

Simon Glass wrote at Wednesday, January 11, 2012 8:00 PM:

Quoted 27 lines · Stephen Warren
Quoted 12 lines · Simon Glass
Quoted 1 line

This enables I2C on Seaboard.

...

Quoted 1 line
include/configs/seaboard.h +0 -0

...

Quoted 1 line

+#define CONFIG_SYS_I2C_INIT_BOARD

I don't think that option is correct for Seaboard; the description in the README indicates this causes a function named i2c_init_board() to be called from boards/xxx/board.c, which is supposed to use GPIOs to unhang the I2C bus. However, this raises a couple of issues:

Quoted 6 lines

1) Patch 5 in this series calls i2c_init_board() ifdef CONFIG_TEGRA2_I2C rather than depending on this CONFIG option.

2) Tegra's i2c_init_board() doesn't appear to be anything to do with bus unhanging, but is instead regular I2C initialization. Perhaps the function should be renamed?

I have had a bit of a look at this. From what I can see the problem is that i2c_init() is passed a bus speed, but this is just CONFIG_SYS_I2C_SPEED. We want to control the speed individually for each port. Yes would could use i2c_init() and ignore the speed, but that seems odd also. At least with the way it is we don't ignore the setting.

Quoted 5 lines
Quoted 2 lines

We also don't define CONFIG_HARD_I2C which again seems odd, but I suppose is for the same reason (we don't want to call i2c_init()).

Hmm. It sounds like we should replace CONFIG_TEGRA2_I2C with CONFIG_HARD_I2C given the README description of the latter?

Well we could, but we would need to ignore the speed argument. Do you think that is better?

Quoted 10 lines
Quoted 9 lines

Also U-Boot tends to call i2c_init() before every use of i2c, whereas we just want to init once and be done with it.

I think the function name is correct, but perhaps I should change the #ifdef you mention in 1 above to CONFIG_SYS_I2C_INIT_BOARD. But for i2c to function on Tegra boards, this must be defined, so in fact this might be counterproductive. So perhaps a check that it is defined is best?

Quoted 4 lines

But README explicitly says that i2c_init_board() is for bus unhanging which isn't what the Tegra implementation does. How can the function name be correct given that?

Well we should rename the function IMO. It seems to me that with a a name like that it is for initing i2c on the board.

Quoted 5 lines

Don't we just want to make i2c_init() use a global/static variable to determine whether the device has already been initialized, and defer all the init until first usage or something like that? That seems in line with U-Boot's desire not to initialize drivers until they're actually used.

Actually that might work - if we keep i2c_init() a no-op, and wait until we get a request for a bus before we look up the fdt and init that port. But I suspect we might need to init port 0 immediately since there is no explicit call to i2c_set_bus_num() for that. It's a little wonky whatever we do. What do you think?

(have just sent the series again with fdt changes, but can update once we sort this out).

Regards,
Simon

#36 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

Simon Glass wrote at Thursday, January 12, 2012 12:10 PM:

Quoted 2 lines · Stephen Warren
Quoted 1 line

Simon Glass wrote at Wednesday, January 11, 2012 8:00 PM:

...

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

Also U-Boot tends to call i2c_init() before every use of i2c, whereas we just want to init once and be done with it.

I think the function name is correct, but perhaps I should change the #ifdef you mention in 1 above to CONFIG_SYS_I2C_INIT_BOARD. But for i2c to function on Tegra boards, this must be defined, so in fact this might be counterproductive. So perhaps a check that it is defined is best?

Quoted 6 lines
Quoted 3 lines

But README explicitly says that i2c_init_board() is for bus unhanging which isn't what the Tegra implementation does. How can the function name be correct given that?

Well we should rename the function IMO. It seems to me that with a a name like that it is for initing i2c on the board.

You mean change the function that's activated by CONFIG_SYS_I2C_INIT_BOARD to be something more like i2c_unhang_board()? That makes sense to me. I have no idea how much of a hassle it'd be to update any existing boards.

Quoted 11 lines
Quoted 5 lines

Don't we just want to make i2c_init() use a global/static variable to determine whether the device has already been initialized, and defer all the init until first usage or something like that? That seems in line with U-Boot's desire not to initialize drivers until they're actually used.

Actually that might work - if we keep i2c_init() a no-op, and wait until we get a request for a bus before we look up the fdt and init that port. But I suspect we might need to init port 0 immediately since there is no explicit call to i2c_set_bus_num() for that. It's a little wonky whatever we do. What do you think?

It does make sense to me to at least parse out all the DT stuff early on, so there's a single place to do all the alias processing etc. Given what I just wrote in the other email about fixing the I2C bus speed stuff, would making those changes here help this at all?

Signature · 1 line
nvpublic
#37 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

Hi Stephen,

Quoted 17 lines · Stephen Warren

Simon Glass wrote at Thursday, January 12, 2012 12:10 PM:

Quoted 2 lines · Stephen Warren
Quoted 1 line

Simon Glass wrote at Wednesday, January 11, 2012 8:00 PM:

...

Quoted 13 lines
Quoted 12 lines
Quoted 8 lines

Also U-Boot tends to call i2c_init() before every use of i2c, whereas we just want to init once and be done with it.

I think the function name is correct, but perhaps I should change the #ifdef you mention in 1 above to CONFIG_SYS_I2C_INIT_BOARD. But for i2c to function on Tegra boards, this must be defined, so in fact this might be counterproductive. So perhaps a check that it is defined is best?

But README explicitly says that i2c_init_board() is for bus unhanging which isn't what the Tegra implementation does. How can the function name be correct given that?

Quoted 6 lines
Quoted 2 lines

Well we should rename the function IMO. It seems to me that with a a name like that it is for initing i2c on the board.

You mean change the function that's activated by CONFIG_SYS_I2C_INIT_BOARD to be something more like i2c_unhang_board()? That makes sense to me. I have no idea how much of a hassle it'd be to update any existing boards.

Well we should change the README or change the function name. I'm not sure which is correct.

Quoted 13 lines
Quoted 7 lines
Quoted 6 lines

Don't we just want to make i2c_init() use a global/static variable to determine whether the device has already been initialized, and defer all the init until first usage or something like that? That seems in line with U-Boot's desire not to initialize drivers until they're actually used.

Quoted 5 lines

Actually that might work - if we keep i2c_init() a no-op, and wait until we get a request for a bus before we look up the fdt and init that port. But I suspect we might need to init port 0 immediately since there is no explicit call to i2c_set_bus_num() for that. It's a little wonky whatever we do. What do you think?

Quoted 4 lines

It does make sense to me to at least parse out all the DT stuff early on, so there's a single place to do all the alias processing etc. Given what I just wrote in the other email about fixing the I2C bus speed stuff, would making those changes here help this at all?

I think the device tree processing should be done later because i2c_init() is called very early, before the dcache is enabled. It is very s l o w to touch device tree before then. To be honest, that's another reason for leaving it as it is, with i2c_init() being a no-op. But we might be able to init the bus on first use with enough if() statements :-)

I will take a look. I am not going to refactor U-Boot I2C here, although I think it could use a few tweaked...

Regards,
Simon

Quoted 4 lines

--
nvpublic

#38 lore raw

PATCH 7/7 tegra: Enable I2C on Seaboard

In reply to Simon Glass

Hello Simon,

Quoted 31 lines · Simon Glass

+U-Boot, which I seemed to have dropped by mistake

Hi Stephen,

Quoted 26 lines · Stephen Warren

Simon Glass wrote at Wednesday, January 11, 2012 8:00 PM:

Quoted 25 lines · Stephen Warren
Quoted 11 lines · Simon Glass
Quoted 1 line

This enables I2C on Seaboard.

...

Quoted 1 line
include/configs/seaboard.h +0 -0

...

Quoted 1 line

+#define CONFIG_SYS_I2C_INIT_BOARD

I don't think that option is correct for Seaboard; the description in the README indicates this causes a function named i2c_init_board() to be called from boards/xxx/board.c, which is supposed to use GPIOs to unhang the I2C bus. However, this raises a couple of issues:

Quoted 6 lines

1) Patch 5 in this series calls i2c_init_board() ifdef CONFIG_TEGRA2_I2C rather than depending on this CONFIG option.

2) Tegra's i2c_init_board() doesn't appear to be anything to do with bus unhanging, but is instead regular I2C initialization. Perhaps the function should be renamed?

I have had a bit of a look at this. From what I can see the problem is that i2c_init() is passed a bus speed, but this is just CONFIG_SYS_I2C_SPEED. We want to control the speed individually for each port. Yes would could use i2c_init() and ignore the speed, but that seems odd also. At least with the way it is we don't ignore the setting.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

We also don't define CONFIG_HARD_I2C which again seems odd, but I suppose is for the same reason (we don't want to call i2c_init()).

Hmm. It sounds like we should replace CONFIG_TEGRA2_I2C with CONFIG_HARD_I2C given the README description of the latter?

Well we could, but we would need to ignore the speed argument. Do you think that is better?

Without defining CONFIG_HARD_i2C, i2c_init() never gets called ... naming CONFIG_TEGRA2_I2C is absolutely OK for enabling the tegra i2c driver.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Also U-Boot tends to call i2c_init() before every use of i2c, whereas we just want to init once and be done with it.

I think the function name is correct, but perhaps I should change the #ifdef you mention in 1 above to CONFIG_SYS_I2C_INIT_BOARD. But for i2c to function on Tegra boards, this must be defined, so in fact this might be counterproductive. So perhaps a check that it is defined is best?

Quoted 6 lines
Quoted 4 lines

But README explicitly says that i2c_init_board() is for bus unhanging which isn't what the Tegra implementation does. How can the function name be correct given that?

Well we should rename the function IMO. It seems to me that with a a name like that it is for initing i2c on the board.

Yes, the name is missleading. On the other hand is a deblocking also some sort of "init" ... but I see no problem in using the i2c_init_board() for doing board specific i2c inits too ... maybe the README should be adapted ... but this problem results, that you don't want to use i2c_init() ...

Quoted 5 lines
Quoted 5 lines

Don't we just want to make i2c_init() use a global/static variable to determine whether the device has already been initialized, and defer all the init until first usage or something like that? That seems in line with U-Boot's desire not to initialize drivers until they're actually used.

Yep, that would be inline with that ... but how would you use "i2c reset" command? That only calls i2c_init() ... so, you never can reset/reinit your i2c bus controller.

Quoted 5 lines

Actually that might work - if we keep i2c_init() a no-op, and wait until we get a request for a bus before we look up the fdt and init that port. But I suspect we might need to init port 0 immediately since there is no explicit call to i2c_set_bus_num() for that. It's a little wonky whatever we do. What do you think?

Yep, the clean way would be to use the multibus/multiadapter branch:

http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/multibus_v2_20111112

but as I said in a previous EMail ... there is some work to do, before it is mainline acceptable ...

[...]

bye,
Heiko

Signature · 2 lines
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany