[U-Boot] [PATCH v2 00/40] ARM: tegra: Add PCIe support

From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
Patch 1 adds a %pa modifier to printf() to print physical addresses. This is required to allow code to output such addresses irrespective of whether a 64 bit or 32 bit architecture is built for.
Patches 2-7 add various FDT helpers to make it easier to parse complex device trees.
Patch 8 is a minor cleanup to the PCI command that prevents a spew of error messages if a bus does not exist. Patch 9 modifies the PCI enumeration code to honor theh restrictions encoded within a host controller driver's pci_ski_dev() implementation. This is required to prevent exceptions from the NVIDIA Tegra PCIe controller.
Patch 10 imports the pr_fmt() macro used within the Linux kernel to reduce the number of characters consumed by literal strings by allowing a source file to specify a prefix or suffix that should be applied to all format strings in the file. It is currently used by the debug() and error() macros.
Patches 11-14 are some cleanup and refactoring of I2C core code, the addition of a higher level API that makes it easier for I2C client drivers to talk to devices. The Tegra I2C driver now implements i2c_get_bus_num_fdt() to obtain bus numbers corresponding to a DT node.
Patch 15 implements a driver for the AS3722 PMIC used on the Venice2 and Jetson TK1 boards.
Patches 16-21 are preparatory work for the Tegra PCIe controller. They add missing clock driver functionality as well as drivers for the Tegra powergate and XUSB pad controller blocks.
Patch 22 adds the PCIe controller driver for Tegra20, Tegra30 and Tegra124.
Device tree nodes and configurations options to enable PCIe on the TrimSlice (Tegra20), Beaver, Cardhu (Tegra30) and Jetson TK1 (Tegra124) boards are added in patches 23-31.
Patches 32-35 implement non-cached memory support that will be used in the last batch of patches to implement more reliable packet transfers in the r8169 driver. Patch 36 enables non-cached memory support on Tegra.
Finally, patches 37-40 implement non-cached memory support and various fixes in the r8169 driver and add support for the revision of the NIC found on the Jetson TK1.
The above boards all have an ethernet NIC connected to PCIe, which is what I tested with.
Thierry Reding (40): vsprintf: Add modifier for phys_addr_t fdt: Add a function to count strings fdt: Add a function to get the index of a string fdt: Add functions to retrieve strings fdt: Add resource parsing functions fdt: Add a function to return PCI BDF triplet fdt: Add a subnodes iterator macro pci: Abort early if bus does not exist pci: Honour pci_skip_dev() Add pr_fmt() macro i2c: Initialize the correct bus i2c: Refactor adapter initialization i2c: Add high-level API i2c: tegra: Implement i2c_get_bus_num_fdt() power: Add AMS AS3722 PMIC support ARM: tegra: Implement tegra_plle_enable() ARM: tegra: Provide PCIEXCLK reset ID ARM: tegra: Implement powergate support ARM: tegra: Implement XUSB pad controller ARM: tegra: Add XUSB pad controller on Tegra124 ARM: tegra: Enable XUSB pad controller on Jetson TK1 pci: tegra: Add Tegra PCIe driver ARM: tegra: Add Tegra20 PCIe device tree node ARM: tegra: Enable PCIe on TrimSlice ARM: tegra: Add GIC for Tegra30 ARM: tegra: Add Tegra30 PCIe device tree node ARM: tegra: Enable PCIe on Cardhu ARM: tegra: Enable PCIe on Beaver ARM: tegra: Add GIC for Tegra124 ARM: tegra: Add Tegra124 PCIe device tree node ARM: tegra: Enable PCIe on Jetson TK1 ARM: cache_v7: Various minor cleanups ARM: cache-cp15: Use more accurate types malloc: Output region when debugging ARM: Implement non-cached memory support ARM: tegra: Enable non-cached memory net: rtl8169: Honor CONFIG_SYS_RX_ETH_BUFFER net: rtl8169: Properly align buffers net: rtl8169: Use non-cached memory if available net: rtl8169: Add support for RTL-8168/8111g
README | 19 + arch/arm/cpu/armv7/cache_v7.c | 14 +- arch/arm/cpu/tegra-common/Makefile | 2 + arch/arm/cpu/tegra-common/powergate.c | 102 ++ arch/arm/cpu/tegra-common/xusb-padctl.c | 39 + arch/arm/cpu/tegra124-common/Makefile | 1 + arch/arm/cpu/tegra124-common/clock.c | 109 +++ arch/arm/cpu/tegra124-common/xusb-padctl.c | 716 ++++++++++++++ arch/arm/cpu/tegra20-common/clock.c | 141 ++- arch/arm/cpu/tegra30-common/clock.c | 155 +++ arch/arm/dts/tegra124-jetson-tk1.dts | 373 +++++++ arch/arm/dts/tegra124.dtsi | 90 ++ arch/arm/dts/tegra20-trimslice.dts | 69 ++ arch/arm/dts/tegra20.dtsi | 60 ++ arch/arm/dts/tegra30-beaver.dts | 245 +++++ arch/arm/dts/tegra30-cardhu.dts | 362 +++++++ arch/arm/dts/tegra30.dtsi | 84 ++ arch/arm/include/asm/arch-tegra/powergate.h | 38 + arch/arm/include/asm/arch-tegra/xusb-padctl.h | 24 + arch/arm/include/asm/arch-tegra114/powergate.h | 6 + arch/arm/include/asm/arch-tegra124/clock.h | 2 + arch/arm/include/asm/arch-tegra124/powergate.h | 6 + arch/arm/include/asm/arch-tegra20/clock-tables.h | 2 +- arch/arm/include/asm/arch-tegra20/clock.h | 2 + arch/arm/include/asm/arch-tegra20/powergate.h | 6 + arch/arm/include/asm/arch-tegra30/clock.h | 2 + arch/arm/include/asm/arch-tegra30/powergate.h | 6 + arch/arm/include/asm/system.h | 7 +- arch/arm/lib/cache-cp15.c | 6 +- arch/arm/lib/cache.c | 42 + board/compulab/trimslice/trimslice.c | 8 + board/nvidia/cardhu/cardhu.c | 56 ++ board/nvidia/common/board.c | 3 + board/nvidia/jetson-tk1/jetson-tk1.c | 52 + common/board_r.c | 11 + common/cmd_pci.c | 7 + common/dlmalloc.c | 3 + drivers/i2c/i2c_core.c | 80 +- drivers/i2c/tegra_i2c.c | 13 + drivers/net/rtl8169.c | 127 ++- drivers/pci/Makefile | 1 + drivers/pci/pci.c | 3 + drivers/pci/pci_tegra.c | 1143 ++++++++++++++++++++++ drivers/power/Makefile | 1 + drivers/power/as3722.c | 300 ++++++ include/common.h | 14 +- include/configs/beaver.h | 10 + include/configs/cardhu.h | 10 + include/configs/jetson-tk1.h | 13 + include/configs/tegra-common.h | 1 + include/configs/trimslice.h | 10 + include/dt-bindings/clock/tegra124-car.h | 341 +++++++ include/dt-bindings/clock/tegra20-car.h | 158 +++ include/dt-bindings/clock/tegra30-car.h | 265 +++++ include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 + include/fdtdec.h | 69 ++ include/i2c.h | 96 ++ include/libfdt.h | 72 ++ include/pci.h | 1 + include/power/as3722.h | 27 + lib/fdtdec.c | 76 ++ lib/libfdt/fdt_ro.c | 76 ++ lib/vsprintf.c | 16 +- 63 files changed, 5736 insertions(+), 64 deletions(-) create mode 100644 arch/arm/cpu/tegra-common/powergate.c create mode 100644 arch/arm/cpu/tegra-common/xusb-padctl.c create mode 100644 arch/arm/cpu/tegra124-common/xusb-padctl.c create mode 100644 arch/arm/include/asm/arch-tegra/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra114/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra124/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra20/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra30/powergate.h create mode 100644 drivers/pci/pci_tegra.c create mode 100644 drivers/power/as3722.c create mode 100644 include/dt-bindings/clock/tegra124-car.h create mode 100644 include/dt-bindings/clock/tegra20-car.h create mode 100644 include/dt-bindings/clock/tegra30-car.h create mode 100644 include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h create mode 100644 include/power/as3722.h

From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0;
printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
Signed-off-by: Thierry Reding treding@nvidia.com --- lib/vsprintf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 7ec758e40fc5..044d5551bdd0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -518,6 +518,8 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) { + u64 num = (uintptr_t)ptr; + /* * Being a boot loader, we explicitly allow pointers to * (physical) address null. @@ -530,6 +532,17 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
#ifdef CONFIG_CMD_NET switch (*fmt) { + case 'a': + flags |= SPECIAL | ZEROPAD; + + switch (fmt[1]) { + case 'p': + default: + field_width = sizeof(phys_addr_t) * 2 + 2; + num = *(phys_addr_t *)ptr; + break; + } + break; case 'm': flags |= SPECIAL; /* Fallthrough */ @@ -555,8 +568,7 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, field_width = 2*sizeof(void *); flags |= ZEROPAD; } - return number(buf, end, (unsigned long)ptr, 16, field_width, - precision, flags); + return number(buf, end, num, 16, field_width, precision, flags); }
static int vsnprintf_internal(char *buf, size_t size, const char *fmt,

On 08/26/2014 09:33 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0;
printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
The series,
Tested-by: Stephen Warren swarren@nvidia.com
Note that I did see the following printed a couple of times when I executed "run bootcmd_pxe":
pci_hose_bus_to_phys: invalid physical address
... but everything worked perfectly, so I guess we can track that down later.

On Tue, Aug 26, 2014 at 11:04:56AM -0600, Stephen Warren wrote:
On 08/26/2014 09:33 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0;
printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
The series,
Tested-by: Stephen Warren swarren@nvidia.com
Note that I did see the following printed a couple of times when I executed "run bootcmd_pxe":
pci_hose_bus_to_phys: invalid physical address
... but everything worked perfectly, so I guess we can track that down later.
Yes, it should definitely be tracked down. I don't see that message on my setup. I've seen it for example when noncached_alloc() fails and returns 0, but in that case everything shouldn't be working perfectly.
It would be helpful if that message showed what physical address was considered invalid.
Thierry

On 08/27/2014 01:01 AM, Thierry Reding wrote:
On Tue, Aug 26, 2014 at 11:04:56AM -0600, Stephen Warren wrote:
On 08/26/2014 09:33 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0;
printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
The series,
Tested-by: Stephen Warren swarren@nvidia.com
Note that I did see the following printed a couple of times when I executed "run bootcmd_pxe":
pci_hose_bus_to_phys: invalid physical address
... but everything worked perfectly, so I guess we can track that down later.
Yes, it should definitely be tracked down. I don't see that message on my setup. I've seen it for example when noncached_alloc() fails and returns 0, but in that case everything shouldn't be working perfectly.
It looks like it happens when "dhcp" or "pxe get" attempt to download a file that doesn't exist on the server.
It would be helpful if that message showed what physical address was considered invalid.
The address is 0. I think this is because rtl_recv() is being called after rtl_halt(). rtl_halt() NULLs out all tpc->RxBufferRing[] entries, so calling rtl_recv() after that tries to convert address 0 to a PCI address. I haven't fully tracked this down, but it seems like a bug in the U-Boot networking or TFTP core, rather than anything to do with drivers for R8169 or the Tegra PCIe controller.
I'm not planning on tracking this down any further at the moment.

On Wed, Aug 27, 2014 at 11:41:55AM -0600, Stephen Warren wrote:
On 08/27/2014 01:01 AM, Thierry Reding wrote:
On Tue, Aug 26, 2014 at 11:04:56AM -0600, Stephen Warren wrote:
On 08/26/2014 09:33 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0;
printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
The series,
Tested-by: Stephen Warren swarren@nvidia.com
Note that I did see the following printed a couple of times when I executed "run bootcmd_pxe":
pci_hose_bus_to_phys: invalid physical address
... but everything worked perfectly, so I guess we can track that down later.
Yes, it should definitely be tracked down. I don't see that message on my setup. I've seen it for example when noncached_alloc() fails and returns 0, but in that case everything shouldn't be working perfectly.
It looks like it happens when "dhcp" or "pxe get" attempt to download a file that doesn't exist on the server.
It would be helpful if that message showed what physical address was considered invalid.
The address is 0. I think this is because rtl_recv() is being called after rtl_halt(). rtl_halt() NULLs out all tpc->RxBufferRing[] entries, so calling rtl_recv() after that tries to convert address 0 to a PCI address. I haven't fully tracked this down, but it seems like a bug in the U-Boot networking or TFTP core, rather than anything to do with drivers for R8169 or the Tegra PCIe controller.
I'm not planning on tracking this down any further at the moment.
The attached patch fixes this issue for me. Cc'ing Joe to help figure out if that's the right approach.
Thierry

From: Thierry Reding treding@nvidia.com Date: Thu, 28 Aug 2014 12:26:58 +0200 Subject: [PATCH] rtl8169: Defer network packet processing
When network protocol errors occur (such as a file not being found on a TFTP server), the processing done by the NetReceive() function will end up calling the driver's .halt() implementation. However, after that the device no longer has access to the memory buffers and will cause errors such as this in the rtl_recv() function when trying to hand descriptors back to the device:
pci_hose_bus_to_phys: invalid physical address
This can be fixed by deferring processing of network packets until the descriptors have been handed back. That way rtl_halt() tearing down network buffers is not going to prevent access to the buffers.
Reported-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com
Applied to u-boot/master, thanks!

Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0; printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
Would it be better to use %#pa to get the 0x prefix so we have the option? Hex is the default in U-Boot.
Signed-off-by: Thierry Reding treding@nvidia.com
lib/vsprintf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 7ec758e40fc5..044d5551bdd0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -518,6 +518,8 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) {
u64 num = (uintptr_t)ptr;
Will this impact code size much? I suppose it is vsprintf() so it doesn't matter too much?
/* * Being a boot loader, we explicitly allow pointers to * (physical) address null.
@@ -530,6 +532,17 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
#ifdef CONFIG_CMD_NET switch (*fmt) {
case 'a':
flags |= SPECIAL | ZEROPAD;
switch (fmt[1]) {
case 'p':
default:
field_width = sizeof(phys_addr_t) * 2 + 2;
num = *(phys_addr_t *)ptr;
break;
}
break; case 'm': flags |= SPECIAL; /* Fallthrough */
@@ -555,8 +568,7 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, field_width = 2*sizeof(void *); flags |= ZEROPAD; }
return number(buf, end, (unsigned long)ptr, 16, field_width,
precision, flags);
return number(buf, end, num, 16, field_width, precision, flags);
}
static int vsnprintf_internal(char *buf, size_t size, const char *fmt,
2.0.4
Regards, Simon

On Tue, Aug 26, 2014 at 05:14:17PM -0600, Simon Glass wrote:
Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0; printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
Would it be better to use %#pa to get the 0x prefix so we have the option? Hex is the default in U-Boot.
Yeah, that's very confusing since there's no way to tell apart a hexadecimal number from a decimal one since there's no decimal equivalent of the 0x prefix.
If you insist I can change that (it should be a simple matter of dropping the SPECIAL flag in the 'a' case).
Signed-off-by: Thierry Reding treding@nvidia.com
lib/vsprintf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 7ec758e40fc5..044d5551bdd0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -518,6 +518,8 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) {
u64 num = (uintptr_t)ptr;
Will this impact code size much? I suppose it is vsprintf() so it doesn't matter too much?
Doing only this change and the corresponding change in the call to number() results in the exact sizes:
$ size build/jetson-tk1.{before,after}/u-boot text data bss dec hex filename 310434 10872 309184 630490 99eda build/jetson-tk1.before/u-boot 310434 10872 309184 630490 99eda build/jetson-tk1.after/u-boot
Given that the num parameter of the number() function is already a u64 the compiler will presumably automatically handle the ptr argument as a 64-bit value.
Thierry

Hi Thierry,
On 27 August 2014 01:37, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 26, 2014 at 05:14:17PM -0600, Simon Glass wrote:
Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Provide a new modifier to vsprintf() to print phys_addr_t variables to avoid having to cast or #ifdef when printing them out. The %pa modifier is used for this purpose, so phys_addr_t variables need to be passed by reference, like so:
phys_addr_t start = 0; printf("start: %pa\n", &start);
Depending on the size of phys_addr_t this will print out the address with 8 or 16 hexadecimal digits following a 0x prefix.
Would it be better to use %#pa to get the 0x prefix so we have the option? Hex is the default in U-Boot.
Yeah, that's very confusing since there's no way to tell apart a hexadecimal number from a decimal one since there's no decimal equivalent of the 0x prefix.
If you insist I can change that (it should be a simple matter of dropping the SPECIAL flag in the 'a' case).
%p doesn't imply %#p, this seems like a unilateral change of behaviour. So I think you should drop the SPECIAL flag.
It might be confusing in some cases but I feel it is quite expected that an 8- or 16-digit value will be hex in U-Boot.
If you wish to change the default input/output base for U-Boot I feel that would require a larger patch.
Signed-off-by: Thierry Reding treding@nvidia.com
lib/vsprintf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 7ec758e40fc5..044d5551bdd0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -518,6 +518,8 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) {
u64 num = (uintptr_t)ptr;
Will this impact code size much? I suppose it is vsprintf() so it doesn't matter too much?
Doing only this change and the corresponding change in the call to number() results in the exact sizes:
$ size build/jetson-tk1.{before,after}/u-boot text data bss dec hex filename 310434 10872 309184 630490 99eda build/jetson-tk1.before/u-boot 310434 10872 309184 630490 99eda build/jetson-tk1.after/u-boot
Given that the num parameter of the number() function is already a u64 the compiler will presumably automatically handle the ptr argument as a 64-bit value.
I hadn't picked that up - I though ulong was 32-bit. Still this code affects most boards.
Regards, Simon
Thierry

From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the fdt_count_strings() function counts the number of strings found in the property value.
Signed-off-by: Thierry Reding treding@nvidia.com --- include/libfdt.h | 9 +++++++++ lib/libfdt/fdt_ro.c | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..cf97bf0b3b52 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -857,6 +857,15 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+/** + * fdt_count_strings - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @node: offset of the node + * @property: name of the property containing the string list + * @return: the number of strings in the given property + */ +int fdt_count_strings(const void *fdt, int node, const char *property); + /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c index 36af0435254b..cb06a9b50d85 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -491,6 +491,26 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) return 0; }
+int fdt_count_strings(const void *fdt, int node, const char *property) +{ + int length, i, count = 0; + const char *list; + + list = fdt_getprop(fdt, node, property, &length); + if (!list) + return -length; + + for (i = 0; i < length; i++) { + int len = strlen(list); + + list += len + 1; + i += len; + count++; + } + + return count; +} + int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) {

Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the fdt_count_strings() function counts the number of strings found in the property value.
Signed-off-by: Thierry Reding treding@nvidia.com
A better name might be fdt_stringlist_count() given that we have fdt_stringlist_contains(). But let's take this and see what happens when you send it upstream.
Acked-by: Simon Glass sjg@chromium.org
Regards, Simon

Applied to u-boot-fdt/next, thanks!

From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the new fdt_find_string() function will look up a given string in the string list contained in the property's value and return its index.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - rename to fdt_find_string() to remove naming conflicts with other new functions - correctly increment string list pointer
include/libfdt.h | 11 +++++++++++ lib/libfdt/fdt_ro.c | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index cf97bf0b3b52..d0dea668eea2 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -866,6 +866,17 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); */ int fdt_count_strings(const void *fdt, int node, const char *property);
+/** + * fdt_find_string - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @node: offset of the node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * @return: the index of the string or negative on error + */ +int fdt_find_string(const void *fdt, int node, const char *property, + const char *string); + /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c index cb06a9b50d85..fec4a0a141fd 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -511,6 +511,32 @@ int fdt_count_strings(const void *fdt, int node, const char *property) return count; }
+int fdt_find_string(const void *fdt, int node, const char *property, + const char *string) +{ + const char *list, *end; + int len, index = 0; + + list = fdt_getprop(fdt, node, property, &len); + if (!list) + return len; + + end = list + len; + len = strlen(string); + + while (list < end) { + int l = strlen(list); + + if (l == len && memcmp(list, string, len) == 0) + return index; + + list += l + 1; + index++; + } + + return -FDT_ERR_NOTFOUND; +} + int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) {

Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the new fdt_find_string() function will look up a given string in the string list contained in the property's value and return its index.
Signed-off-by: Thierry Reding treding@nvidia.com
fdt_stringlist_find() maybe? Will see what happens when you upstream.
Acked-by: Simon Glass sjg@chromium.org
Regards, Simon

Applied to u-boot-fdt/next, thanks!

From: Thierry Reding treding@nvidia.com
Given a device tree node, a property name and an index, the new function fdt_get_string_index() will return in an output argument a pointer to the index'th string in the property's value.
The fdt_get_string() is a shortcut for the above with the index being 0.
Signed-off-by: Thierry Reding treding@nvidia.com --- include/libfdt.h | 27 +++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index d0dea668eea2..2dfc6d9e5ce7 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -877,6 +877,33 @@ int fdt_count_strings(const void *fdt, int node, const char *property); int fdt_find_string(const void *fdt, int node, const char *property, const char *string);
+/** + * fdt_get_string_index() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @node: offset of the node + * @property: name of the property containing the string list + * @index: index of the string to return + * @output: return location for the string + * @return: 0 if the string was found or a negative error code otherwise + */ +int fdt_get_string_index(const void *fdt, int node, const char *property, + int index, const char **output); + +/** + * fdt_get_string() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @node: offset of the node + * @property: name of the property containing the string list + * @output: return location for the string + * @return: 0 if the string was found or a negative error code otherwise + * + * This is a shortcut for: + * + * fdt_get_string_index(fdt, node, property, 0, output). + */ +int fdt_get_string(const void *fdt, int node, const char *property, + const char **output); + /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c index fec4a0a141fd..03733e574f71 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -537,6 +537,36 @@ int fdt_find_string(const void *fdt, int node, const char *property, return -FDT_ERR_NOTFOUND; }
+int fdt_get_string_index(const void *fdt, int node, const char *property, + int index, const char **output) +{ + const char *list; + int length, i; + + list = fdt_getprop(fdt, node, property, &length); + + for (i = 0; i < length; i++) { + int len = strlen(list); + + if (index == 0) { + *output = list; + return 0; + } + + list += len + 1; + i += len; + index--; + } + + return FDT_ERR_NOTFOUND; +} + +int fdt_get_string(const void *fdt, int node, const char *property, + const char **output) +{ + return fdt_get_string_index(fdt, node, property, 0, output); +} + int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) {

Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, a property name and an index, the new function fdt_get_string_index() will return in an output argument a pointer to the index'th string in the property's value.
The fdt_get_string() is a shortcut for the above with the index being 0.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Could be fdt_stringlist_lookup() maybe. But there's no point guessing until you send this upstream.
Regards, Simon

Applied to u-boot-fdt/next, thanks!

Hi Thierry,
On 8 September 2014 at 09:02, Simon Glass sjg@chromium.org wrote:
Applied to u-boot-fdt/next, thanks!
Did you submit these patches to dtc upstream? I don't see them applied.
Regards, SImon

+Scott, Masahiro
Hi Thierry,
On 25 March 2015 at 17:23, Simon Glass sjg@chromium.org wrote:
Hi Thierry,
On 8 September 2014 at 09:02, Simon Glass sjg@chromium.org wrote:
Applied to u-boot-fdt/next, thanks!
Did you submit these patches to dtc upstream? I don't see them applied.
Ping? Masahiro has now sent a series on top of your patches, so we need to sort this out.
Regards, Simon

On Tue, Jul 14, 2015 at 01:48:45PM -0600, Simon Glass wrote:
+Scott, Masahiro
Hi Thierry,
On 25 March 2015 at 17:23, Simon Glass sjg@chromium.org wrote:
Hi Thierry,
On 8 September 2014 at 09:02, Simon Glass sjg@chromium.org wrote:
Applied to u-boot-fdt/next, thanks!
Did you submit these patches to dtc upstream? I don't see them applied.
Ping? Masahiro has now sent a series on top of your patches, so we need to sort this out.
I sent these to the DTC maintainers and the list, with you and Masahiro in Cc. They patches should be eqivalent to what's in U-Boot except for the node parameter that I renamed nodeoffset for consistency with libfdt and the testsuite bits.
Thierry

Hello Thierry,
On Wed, 15 Jul 2015 13:17:18 +0200, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 14, 2015 at 01:48:45PM -0600, Simon Glass wrote:
+Scott, Masahiro
Hi Thierry,
On 25 March 2015 at 17:23, Simon Glass sjg@chromium.org wrote:
Hi Thierry,
On 8 September 2014 at 09:02, Simon Glass sjg@chromium.org wrote:
Applied to u-boot-fdt/next, thanks!
Did you submit these patches to dtc upstream? I don't see them applied.
Ping? Masahiro has now sent a series on top of your patches, so we need to sort this out.
I sent these to the DTC maintainers and the list, with you and Masahiro in Cc. They patches should be eqivalent to what's in U-Boot except for the node parameter that I renamed nodeoffset for consistency with libfdt and the testsuite bits.
Do you mean there are intrinsic incompatibilities between U-Boot and Linux that make it impossible to use the same name in both?
Thierry
Amicalement,

On Wed, Jul 15, 2015 at 01:35:26PM +0200, Albert ARIBAUD wrote:
Hello Thierry,
On Wed, 15 Jul 2015 13:17:18 +0200, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 14, 2015 at 01:48:45PM -0600, Simon Glass wrote:
+Scott, Masahiro
Hi Thierry,
On 25 March 2015 at 17:23, Simon Glass sjg@chromium.org wrote:
Hi Thierry,
On 8 September 2014 at 09:02, Simon Glass sjg@chromium.org wrote:
Applied to u-boot-fdt/next, thanks!
Did you submit these patches to dtc upstream? I don't see them applied.
Ping? Masahiro has now sent a series on top of your patches, so we need to sort this out.
I sent these to the DTC maintainers and the list, with you and Masahiro in Cc. They patches should be eqivalent to what's in U-Boot except for the node parameter that I renamed nodeoffset for consistency with libfdt and the testsuite bits.
Do you mean there are intrinsic incompatibilities between U-Boot and Linux that make it impossible to use the same name in both?
No, I don't think so. Back at the time I must have assumed that node was the consistent name used in U-Boot, but it seems like these string functions are now the only ones in libfdt that use this. I'd expect that to fix itself when libfdt gets sync'ed back from the upstream copy.
Thierry

From: Thierry Reding treding@nvidia.com
Add the fdt_get_resource() and fdt_get_named_resource() functions which can be used to parse resources (memory regions) from an FDT. A helper to compute the size of a region is also provided.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - use existing fdt_address_cells() and fdt_size_cells() functions - add clarifying comments to struct fdt_resource declaration - rename names variable to prop_names for clarity - properly handle 64-bit addresses (compile-tested only) - update for fdt_get_string_index() -> fdt_find_string() rename
include/fdtdec.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/fdtdec.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 856e6cf766de..c35d378602d2 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -40,6 +40,27 @@ struct fdt_memory { fdt_addr_t end; };
+/* + * Information about a resource. start is the first address of the resource + * and end is the last address (inclusive). The length of the resource will + * be equal to: end - start + 1. + */ +struct fdt_resource { + fdt_addr_t start; + fdt_addr_t end; +}; + +/** + * Compute the size of a resource. + * + * @param res the resource to operate on + * @return the size of the resource + */ +static inline fdt_size_t fdt_resource_size(const struct fdt_resource *res) +{ + return res->end - res->start + 1; +} + /** * Compat types that we know about and for which we might have drivers. * Each is named COMPAT_<dir>_<filename> where <dir> is the directory @@ -583,4 +604,35 @@ struct fmap_entry { */ int fdtdec_read_fmap_entry(const void *blob, int node, const char *name, struct fmap_entry *entry); + +/** + * Obtain an indexed resource from a device property. + * + * @param fdt FDT blob + * @param node node to examine + * @param property name of the property to parse + * @param index index of the resource to retrieve + * @param res returns the resource + * @return 0 if ok, negative on error + */ +int fdt_get_resource(const void *fdt, int node, const char *property, + unsigned int index, struct fdt_resource *res); + +/** + * Obtain a named resource from a device property. + * + * Look up the index of the name in a list of strings and return the resource + * at that index. + * + * @param fdt FDT blob + * @param node node to examine + * @param property name of the property to parse + * @param prop_names name of the property containing the list of names + * @param name the name of the entry to look up + * @param res returns the resource + */ +int fdt_get_named_resource(const void *fdt, int node, const char *property, + const char *prop_names, const char *name, + struct fdt_resource *res); + #endif diff --git a/lib/fdtdec.c b/lib/fdtdec.c index eb5aa20526fd..c3fa82f1a28a 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -691,4 +691,61 @@ int fdtdec_read_fmap_entry(const void *blob, int node, const char *name,
return 0; } + +static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) +{ + u64 number = 0; + + while (cells--) + number = (number << 32) | fdt32_to_cpu(*ptr++); + + return number; +} + +int fdt_get_resource(const void *fdt, int node, const char *property, + unsigned int index, struct fdt_resource *res) +{ + const fdt32_t *ptr, *end; + int na, ns, len, parent; + unsigned int i = 0; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) + return parent; + + na = fdt_address_cells(fdt, parent); + ns = fdt_size_cells(fdt, parent); + + ptr = fdt_getprop(fdt, node, property, &len); + if (!ptr) + return len; + + end = ptr + len / sizeof(*ptr); + + while (ptr + na + ns <= end) { + if (i == index) { + res->start = res->end = fdtdec_get_number(ptr, na); + res->end += fdtdec_get_number(&ptr[na], ns) - 1; + return 0; + } + + ptr += na + ns; + i++; + } + + return -FDT_ERR_NOTFOUND; +} + +int fdt_get_named_resource(const void *fdt, int node, const char *property, + const char *prop_names, const char *name, + struct fdt_resource *res) +{ + int index; + + index = fdt_find_string(fdt, node, prop_names, name); + if (index < 0) + return index; + + return fdt_get_resource(fdt, node, property, index, res); +} #endif

On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Add the fdt_get_resource() and fdt_get_named_resource() functions which can be used to parse resources (memory regions) from an FDT. A helper to compute the size of a region is also provided.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org

Applied to u-boot-fdt/next, thanks!

From: Thierry Reding treding@nvidia.com
The fdtdec_pci_get_bdf() function returns the bus, device, function triplet of a PCI device by parsing the "reg" property according to the PCI device tree binding.
Acked-by: Simon Glass sjg@chromium.org Signed-off-by: Thierry Reding treding@nvidia.com --- include/fdtdec.h | 11 +++++++++++ lib/fdtdec.c | 14 ++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index c35d378602d2..5c669a5c8e03 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -635,4 +635,15 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property, const char *prop_names, const char *name, struct fdt_resource *res);
+/** + * Look at the reg property of a device node that represents a PCI device + * and parse the bus, device and function number from it. + * + * @param fdt FDT blob + * @param node node to examine + * @param bdf returns bus, device, function triplet + * @return 0 if ok, negative on error + */ +int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf); + #endif diff --git a/lib/fdtdec.c b/lib/fdtdec.c index c3fa82f1a28a..fa5da0c0147d 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -748,4 +748,18 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property,
return fdt_get_resource(fdt, node, property, index, res); } + +int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf) +{ + const fdt32_t *prop; + int len; + + prop = fdt_getprop(fdt, node, "reg", &len); + if (!prop) + return len; + + *bdf = fdt32_to_cpu(*prop) & 0xffffff; + + return 0; +} #endif

Applied to u-boot-fdt/next, thanks!

From: Thierry Reding treding@nvidia.com
The fdt_for_each_subnode() iterator macro provided by this patch can be used to iterate over a device tree node's subnodes. At each iteration a loop variable will be set to the next subnode.
Acked-by: Simon Glass sjg@chromium.org Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - specify the data types in the comment because the macro doesn't have them
include/libfdt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index 2dfc6d9e5ce7..f3cbb637be41 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -163,6 +163,31 @@ int fdt_first_subnode(const void *fdt, int offset); */ int fdt_next_subnode(const void *fdt, int offset);
+/** + * fdt_for_each_subnode - iterate over all subnodes of a parent + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_subnode(fdt, node, parent) { + * ... + * use node + * ... + * } + * + * Note that this is implemented as a macro and node is used as iterator in + * the loop. It should therefore be a locally allocated variable. The parent + * variable on the other hand is never modified, so it can be constant or + * even a literal. + * + * @fdt: FDT blob (const void *) + * @node: child node (int) + * @parent: parent node (int) + */ +#define fdt_for_each_subnode(fdt, node, parent) \ + for (node = fdt_first_subnode(fdt, parent); \ + node >= 0; \ + node = fdt_next_subnode(fdt, node)) + /**********************************************************************/ /* General functions */ /**********************************************************************/

Applied to u-boot-fdt/next, thanks!

From: Thierry Reding treding@nvidia.com
When listing the devices on a PCI bus, the current code will blindly try to access all devices. Internally this causes pci_bus_to_hose() to be repeatedly called and output an error message every time. Prevent this by calling pci_bus_to_hose() once and abort early if no bus was found.
Signed-off-by: Thierry Reding treding@nvidia.com --- common/cmd_pci.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/common/cmd_pci.c b/common/cmd_pci.c index a1ba42e2f3a2..5a6048c92604 100644 --- a/common/cmd_pci.c +++ b/common/cmd_pci.c @@ -42,12 +42,16 @@ void pci_header_show_brief(pci_dev_t dev); */ void pciinfo(int BusNum, int ShortPCIListing) { + struct pci_controller *hose = pci_bus_to_hose(BusNum); int Device; int Function; unsigned char HeaderType; unsigned short VendorID; pci_dev_t dev;
+ if (!hose) + return; + printf("Scanning PCI devices on bus %d\n", BusNum);
if (ShortPCIListing) {

From: Thierry Reding treding@nvidia.com
When enumerating devices, honour the pci_skip_dev() function. This can be used by PCI controller drivers to restrict which devices will be probed.
This is required by the NVIDIA Tegra PCIe controller driver, which will fail with a data abort exception if an access is attempted to a device number larger than 0 outside of bus 0. pci_skip_dev() is therefore implemented to prevent any such accesses.
Signed-off-by: Thierry Reding treding@nvidia.com --- common/cmd_pci.c | 3 +++ drivers/pci/pci.c | 3 +++ include/pci.h | 1 + 3 files changed, 7 insertions(+)
diff --git a/common/cmd_pci.c b/common/cmd_pci.c index 5a6048c92604..e3a77e35820c 100644 --- a/common/cmd_pci.c +++ b/common/cmd_pci.c @@ -71,6 +71,9 @@ void pciinfo(int BusNum, int ShortPCIListing)
dev = PCI_BDF(BusNum, Device, Function);
+ if (pci_skip_dev(hose, dev)) + continue; + pci_read_config_word(dev, PCI_VENDOR_ID, &VendorID); if ((VendorID == 0xFFFF) || (VendorID == 0x0000)) continue; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ed113bf4022f..61882ba1462f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -195,6 +195,9 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) bdf < PCI_BDF(bus + 1, 0, 0); #endif bdf += PCI_BDF(0, 0, 1)) { + if (pci_skip_dev(hose, bdf)) + continue; + if (!PCI_FUNC(bdf)) { pci_read_config_byte(bdf, PCI_HEADER_TYPE, diff --git a/include/pci.h b/include/pci.h index 461f17c05895..133fff4957b1 100644 --- a/include/pci.h +++ b/include/pci.h @@ -623,6 +623,7 @@ extern void pci_register_hose(struct pci_controller* hose); extern struct pci_controller* pci_bus_to_hose(int bus); extern struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr);
+extern int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev); extern int pci_hose_scan(struct pci_controller *hose); extern int pci_hose_scan_bus(struct pci_controller *hose, int bus);

From: Thierry Reding treding@nvidia.com
This macro can be overridden in source files (before including common.h) and can be used to specify a prefix for debug and error messages. An example of how to use this is shown below:
#define pr_fmt(fmt) "foo: " fmt
#include <common.h>
... debug("bar");
The resulting message will read:
foo: bar
Acked-by: Simon Glass sjg@chromium.org Signed-off-by: Thierry Reding treding@nvidia.com --- include/common.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/include/common.h b/include/common.h index 1d6cb48ff078..65db04b452d7 100644 --- a/include/common.h +++ b/include/common.h @@ -99,15 +99,19 @@ typedef volatile unsigned char vu_char; #define _DEBUG 0 #endif
+#ifndef pr_fmt +#define pr_fmt(fmt) fmt +#endif + /* * Output a debug text when condition "cond" is met. The "cond" should be * computed by a preprocessor in the best case, allowing for the best * optimization. */ -#define debug_cond(cond, fmt, args...) \ - do { \ - if (cond) \ - printf(fmt, ##args); \ +#define debug_cond(cond, fmt, args...) \ + do { \ + if (cond) \ + printf(pr_fmt(fmt), ##args); \ } while (0)
#define debug(fmt, args...) \ @@ -129,7 +133,7 @@ void __assert_fail(const char *assertion, const char *file, unsigned line, __assert_fail(#x, __FILE__, __LINE__, __func__); })
#define error(fmt, args...) do { \ - printf("ERROR: " fmt "\nat %s:%d/%s()\n", \ + printf("ERROR: " pr_fmt(fmt) "\nat %s:%d/%s()\n", \ ##args, __FILE__, __LINE__, __func__); \ } while (0)

From: Thierry Reding treding@nvidia.com
i2c_bus_init() takes a bus number but relies on the currently selected bus to determine which adapter to initialize. Make the function use the bus passed in as parameter rather than the currently selected bus. While at it, keep a pointer to the specified bus to avoid having to look it up repeatedly.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/i2c/i2c_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601c1..cca455bc9c63 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -214,17 +214,20 @@ static int i2c_mux_disconnet_all(void) * Initializes one bus. Will initialize the parent adapter. No current bus * changes, no mux (if any) setup. */ -static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +static void i2c_init_bus(unsigned int bus, int speed, int slaveaddr) { - if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) + struct i2c_adapter *adapter; + + if (bus >= CONFIG_SYS_NUM_I2C_BUSES) return;
- I2C_ADAP->init(I2C_ADAP, speed, slaveaddr); + adapter = i2c_get_adapter(I2C_ADAPTER(bus)); + adapter->init(adapter, speed, slaveaddr);
if (gd->flags & GD_FLG_RELOC) { - I2C_ADAP->init_done = 1; - I2C_ADAP->speed = speed; - I2C_ADAP->slaveaddr = slaveaddr; + adapter->init_done = 1; + adapter->speed = speed; + adapter->slaveaddr = slaveaddr; } }

Hello Thierry,
Am 26.08.2014 17:33, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
i2c_bus_init() takes a bus number but relies on the currently selected bus to determine which adapter to initialize. Make the function use the bus passed in as parameter rather than the currently selected bus. While at it, keep a pointer to the specified bus to avoid having to look it up repeatedly.
Signed-off-by: Thierry Redingtreding@nvidia.com
drivers/i2c/i2c_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
Why you could not use the current CONFIG_SYS_I2C API and init a bus with i2c_set_bus_num()?
i2c_init_bus() is deprecated and should be removed if all i2c drivers are ported to the CONFIG_SYS_I2C framework ...
bye, Heiko
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601c1..cca455bc9c63 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -214,17 +214,20 @@ static int i2c_mux_disconnet_all(void)
- Initializes one bus. Will initialize the parent adapter. No current bus
- changes, no mux (if any) setup.
*/ -static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +static void i2c_init_bus(unsigned int bus, int speed, int slaveaddr) {
- if (bus_no>= CONFIG_SYS_NUM_I2C_BUSES)
- struct i2c_adapter *adapter;
- if (bus>= CONFIG_SYS_NUM_I2C_BUSES) return;
- I2C_ADAP->init(I2C_ADAP, speed, slaveaddr);
adapter = i2c_get_adapter(I2C_ADAPTER(bus));
adapter->init(adapter, speed, slaveaddr);
if (gd->flags& GD_FLG_RELOC) {
I2C_ADAP->init_done = 1;
I2C_ADAP->speed = speed;
I2C_ADAP->slaveaddr = slaveaddr;
adapter->init_done = 1;
adapter->speed = speed;
} }adapter->slaveaddr = slaveaddr;

On Wed, Aug 27, 2014 at 06:52:16AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:33, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
i2c_bus_init() takes a bus number but relies on the currently selected bus to determine which adapter to initialize. Make the function use the bus passed in as parameter rather than the currently selected bus. While at it, keep a pointer to the specified bus to avoid having to look it up repeatedly.
Signed-off-by: Thierry Redingtreding@nvidia.com
drivers/i2c/i2c_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
Why you could not use the current CONFIG_SYS_I2C API and init a bus with i2c_set_bus_num()?
That's orthogonal to this patch. i2c_set_bus_num() will end up calling the i2c_init_bus() function, too. What this patch does is fix an issue where i2c_init_bus is completely ignoring the bus_no parameter (except for sanity checking) but instead relies on the gd->cur_i2c_bus (via I2C_ADAP) to initialize a bus. That's completely unexpected and making this consistent allows the function to be reused in a more generic way as done in subsequent patches.
Thierry
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601c1..cca455bc9c63 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -214,17 +214,20 @@ static int i2c_mux_disconnet_all(void)
- Initializes one bus. Will initialize the parent adapter. No current bus
- changes, no mux (if any) setup.
*/ -static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +static void i2c_init_bus(unsigned int bus, int speed, int slaveaddr) {
- if (bus_no>= CONFIG_SYS_NUM_I2C_BUSES)
- struct i2c_adapter *adapter;
- if (bus>= CONFIG_SYS_NUM_I2C_BUSES) return;
- I2C_ADAP->init(I2C_ADAP, speed, slaveaddr);
adapter = i2c_get_adapter(I2C_ADAPTER(bus));
adapter->init(adapter, speed, slaveaddr);
if (gd->flags& GD_FLG_RELOC) {
I2C_ADAP->init_done = 1;
I2C_ADAP->speed = speed;
I2C_ADAP->slaveaddr = slaveaddr;
adapter->init_done = 1;
adapter->speed = speed;
}adapter->slaveaddr = slaveaddr;
}
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

Hello Thierry,
Am 27.08.2014 07:12, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 06:52:16AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:33, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
i2c_bus_init() takes a bus number but relies on the currently selected bus to determine which adapter to initialize. Make the function use the bus passed in as parameter rather than the currently selected bus. While at it, keep a pointer to the specified bus to avoid having to look it up repeatedly.
Signed-off-by: Thierry Redingtreding@nvidia.com
drivers/i2c/i2c_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
Why you could not use the current CONFIG_SYS_I2C API and init a bus with i2c_set_bus_num()?
That's orthogonal to this patch. i2c_set_bus_num() will end up calling the i2c_init_bus() function, too. What this patch does is fix an issue where i2c_init_bus is completely ignoring the bus_no parameter (except for sanity checking) but instead relies on the gd->cur_i2c_bus (via I2C_ADAP) to initialize a bus. That's completely unexpected and making this consistent allows the function to be reused in a more generic way as done in subsequent patches.
Yes, I think we could drop the bus_no parameter.
bye, Heiko
Thierry
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601c1..cca455bc9c63 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -214,17 +214,20 @@ static int i2c_mux_disconnet_all(void)
- Initializes one bus. Will initialize the parent adapter. No current bus
- changes, no mux (if any) setup.
*/ -static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +static void i2c_init_bus(unsigned int bus, int speed, int slaveaddr) {
- if (bus_no>= CONFIG_SYS_NUM_I2C_BUSES)
- struct i2c_adapter *adapter;
- if (bus>= CONFIG_SYS_NUM_I2C_BUSES) return;
- I2C_ADAP->init(I2C_ADAP, speed, slaveaddr);
adapter = i2c_get_adapter(I2C_ADAPTER(bus));
adapter->init(adapter, speed, slaveaddr);
if (gd->flags& GD_FLG_RELOC) {
I2C_ADAP->init_done = 1;
I2C_ADAP->speed = speed;
I2C_ADAP->slaveaddr = slaveaddr;
adapter->init_done = 1;
adapter->speed = speed;
} }adapter->slaveaddr = slaveaddr;
-- DENX Software Engineering GmbH, MD: Wolfgang Denk& Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

From: Thierry Reding treding@nvidia.com
A subsequent patch will introduce a new API to access I2C adapters directly rather than going through the bus number and constantly looking up the same adapter. In order to share the adapter initialization code, move it into a separate function and make i2c_init_bus() use it to avoid code duplication.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/i2c/i2c_core.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index cca455bc9c63..f6179a16244b 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -207,6 +207,18 @@ static int i2c_mux_disconnet_all(void) } #endif
+static void i2c_adapter_init(struct i2c_adapter *adapter, unsigned int speed, + unsigned int slaveaddr) +{ + adapter->init(adapter, speed, slaveaddr); + + if (gd->flags & GD_FLG_RELOC) { + adapter->slaveaddr = slaveaddr; + adapter->speed = speed; + adapter->init_done = 1; + } +} + /* * i2c_init_bus(): * --------------- @@ -222,13 +234,7 @@ static void i2c_init_bus(unsigned int bus, int speed, int slaveaddr) return;
adapter = i2c_get_adapter(I2C_ADAPTER(bus)); - adapter->init(adapter, speed, slaveaddr); - - if (gd->flags & GD_FLG_RELOC) { - adapter->init_done = 1; - adapter->speed = speed; - adapter->slaveaddr = slaveaddr; - } + i2c_adapter_init(adapter, speed, slaveaddr); }
/* implement possible board specific board init */

From: Thierry Reding treding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
Drivers for I2C devices are supposed to embed a struct i2c_client within a driver-specific data structure and call i2c_client_init() on it, passing in a pointer to the parent I2C adapter and the slave address of the device.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/i2c/i2c_core.c | 53 ++++++++++++++++++++++++++++ include/i2c.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+)
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index f6179a16244b..88c7af546b0b 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -410,3 +410,56 @@ void __i2c_init(int speed, int slaveaddr) } void i2c_init(int speed, int slaveaddr) __attribute__((weak, alias("__i2c_init"))); + +struct i2c_adapter *i2c_adapter_get(unsigned int index) +{ + struct i2c_adapter *adapter = ll_entry_start(struct i2c_adapter, i2c); + unsigned int num = ll_entry_count(struct i2c_adapter, i2c); + unsigned int i; + + if (index >= num) + return NULL; + + for (i = 0; i < index; i++) + adapter++; + + i2c_adapter_init(adapter, adapter->speed, adapter->slaveaddr); + return adapter; +} + +int i2c_adapter_read(struct i2c_adapter *adapter, uint8_t chip, + unsigned int address, size_t alen, void *buffer, + size_t size) +{ + return adapter->read(adapter, chip, address, alen, buffer, size); +} + +int i2c_adapter_write(struct i2c_adapter *adapter, uint8_t chip, + unsigned int address, size_t alen, void *buffer, + size_t size) +{ + return adapter->write(adapter, chip, address, alen, buffer, size); +} + +int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter, + uint8_t address) +{ + client->adapter = adapter; + client->address = address; + + return 0; +} + +int i2c_client_read(struct i2c_client *client, unsigned int address, + size_t alen, void *buffer, size_t size) +{ + return i2c_adapter_read(client->adapter, client->address, address, + alen, buffer, size); +} + +int i2c_client_write(struct i2c_client *client, unsigned int address, + size_t alen, void *buffer, size_t size) +{ + return i2c_adapter_write(client->adapter, client->address, address, + alen, buffer, size); +} diff --git a/include/i2c.h b/include/i2c.h index 1b4078ed62fe..8f73ba93c614 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -55,6 +55,20 @@ #define CONFIG_SYS_SPD_BUS_NUM 0 #endif
+/** + * struct i2c_adapter - I2C adapter + * @init: initialize I2C adapter + * @probe: probe for a device on the I2C bus + * @read: read data from a device on the I2C bus + * @write: write data to a device on the I2C bus + * @set_bus_speed: configure the I2C bus speed + * @speed: current I2C bus speed + * @waitdelay: + * @slaveaddr: own address (when used as a slave) + * @init_done: flag to indicate whether or not the adapter has been initialized + * @hwadapnr: the hardware number of this adapter + * @name: name of this adapter + */ struct i2c_adapter { void (*init)(struct i2c_adapter *adap, int speed, int slaveaddr); @@ -75,6 +89,16 @@ struct i2c_adapter { char *name; };
+/** + * struct i2c_client - I2C slave + * @adapter: I2C adapter providing the slave's parent bus + * address: address of the I2C slave on the parent bus + */ +struct i2c_client { + struct i2c_adapter *adapter; + unsigned int address; +}; + #define U_BOOT_I2C_MKENT_COMPLETE(_init, _probe, _read, _write, \ _set_speed, _speed, _slaveaddr, _hwadapnr, _name) \ { \ @@ -271,6 +295,78 @@ unsigned int i2c_get_bus_speed(void); * Adjusts I2C pointers after U-Boot is relocated to DRAM */ void i2c_reloc_fixup(void); + +/* + * i2c_adapter_get() - get the I2C adapter associated with a given index + * @index: index of the I2C adapter + */ +struct i2c_adapter *i2c_adapter_get(unsigned int index); + +/* + * i2c_adapter_read() - read data from an I2C slave at a given address + * @adapter: I2C adapter + * @chip: address of the I2C slave to read from + * @address: address within the I2C slave to read from + * @alen: length of address + * @buffer: buffer to receive data from I2C slave + * @size: number of bytes to read + */ +int i2c_adapter_read(struct i2c_adapter *adapter, uint8_t chip, + unsigned int address, size_t alen, void *buffer, + size_t size); + +/* + * i2c_adapter_write() - write data to an I2C slave at a given address + * @adapter: I2C adapter + * @chip: address of the I2C slave to write to + * @address: address within the I2C slave to write to + * @alen: length of address + * @buffer: buffer containing the data to write + * @size: number of bytes to write + * + * Ideally the function would take a const void * buffer, but the underlying + * infrastructure doesn't properly propagate const and adding it here would + * cause a lot of build warnings. + */ +int i2c_adapter_write(struct i2c_adapter *adapter, uint8_t chip, + unsigned int address, size_t alen, void *buffer, + size_t size); + +/* + * i2c_client_init() - initialize an I2C slave + * @client: I2C slave + * @adapter: parent I2C adapter + * @address: address of I2C slave + */ +int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter, + uint8_t address); + +/* + * i2c_client_read() - read data from an I2C slave + * @client: I2C slave + * @address: address within the I2C slave to read from + * @alen: length of address + * @buffer: buffer to receive data from I2C slave + * @size: number of bytes to read + */ +int i2c_client_read(struct i2c_client *client, unsigned int address, + size_t alen, void *buffer, size_t size); + +/* + * i2c_client_write() - write data to an I2C slave + * @client: I2C slave + * @address: address within the I2C slave to write to + * @alen: length of address + * @buffer: buffer containing the data to write + * @size: number of bytes to write + * + * Ideally the function would take a const void * buffer, but the underlying + * infrastructure doesn't properly propagate const and adding it here would + * cause a lot of build warnings. + */ +int i2c_client_write(struct i2c_client *client, unsigned int address, + size_t alen, void *buffer, size_t size); + #if defined(CONFIG_SYS_I2C_SOFT) void i2c_soft_init(void); void i2c_soft_active(void);

Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
Maybe there is on one i2c adapter a i2c mux with 4 ports. On one is an eeprom, on the other is a PMIC ... your code in patch "power: Add AMS AS3722 PMIC support" does access with your functions the PMIC ... what is, if between this accesses someone accesses the eeprom? If he switches the mux, you never switch back!
Your code did not check this!
Why is i2c_set_bus_num() such a problem?
Drivers for I2C devices are supposed to embed a struct i2c_client within a driver-specific data structure and call i2c_client_init() on it, passing in a pointer to the parent I2C adapter and the slave address of the device.
Signed-off-by: Thierry Redingtreding@nvidia.com
drivers/i2c/i2c_core.c | 53 ++++++++++++++++++++++++++++ include/i2c.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+)
diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index f6179a16244b..88c7af546b0b 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -410,3 +410,56 @@ void __i2c_init(int speed, int slaveaddr) } void i2c_init(int speed, int slaveaddr) __attribute__((weak, alias("__i2c_init")));
+struct i2c_adapter *i2c_adapter_get(unsigned int index) +{
- struct i2c_adapter *adapter = ll_entry_start(struct i2c_adapter, i2c);
- unsigned int num = ll_entry_count(struct i2c_adapter, i2c);
- unsigned int i;
- if (index>= num)
return NULL;
- for (i = 0; i< index; i++)
adapter++;
- i2c_adapter_init(adapter, adapter->speed, adapter->slaveaddr);
- return adapter;
+}
+int i2c_adapter_read(struct i2c_adapter *adapter, uint8_t chip,
unsigned int address, size_t alen, void *buffer,
size_t size)
+{
- return adapter->read(adapter, chip, address, alen, buffer, size);
+}
+int i2c_adapter_write(struct i2c_adapter *adapter, uint8_t chip,
unsigned int address, size_t alen, void *buffer,
size_t size)
+{
- return adapter->write(adapter, chip, address, alen, buffer, size);
+}
+int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter,
uint8_t address)
+{
- client->adapter = adapter;
- client->address = address;
- return 0;
+}
+int i2c_client_read(struct i2c_client *client, unsigned int address,
size_t alen, void *buffer, size_t size)
+{
- return i2c_adapter_read(client->adapter, client->address, address,
alen, buffer, size);
+}
+int i2c_client_write(struct i2c_client *client, unsigned int address,
size_t alen, void *buffer, size_t size)
+{
- return i2c_adapter_write(client->adapter, client->address, address,
alen, buffer, size);
+} diff --git a/include/i2c.h b/include/i2c.h index 1b4078ed62fe..8f73ba93c614 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -55,6 +55,20 @@ #define CONFIG_SYS_SPD_BUS_NUM 0 #endif
+/**
- struct i2c_adapter - I2C adapter
- @init: initialize I2C adapter
- @probe: probe for a device on the I2C bus
- @read: read data from a device on the I2C bus
- @write: write data to a device on the I2C bus
- @set_bus_speed: configure the I2C bus speed
- @speed: current I2C bus speed
- @waitdelay:
- @slaveaddr: own address (when used as a slave)
- @init_done: flag to indicate whether or not the adapter has been initialized
- @hwadapnr: the hardware number of this adapter
- @name: name of this adapter
- */ struct i2c_adapter { void (*init)(struct i2c_adapter *adap, int speed, int slaveaddr);
@@ -75,6 +89,16 @@ struct i2c_adapter { char *name; };
+/**
- struct i2c_client - I2C slave
- @adapter: I2C adapter providing the slave's parent bus
- address: address of the I2C slave on the parent bus
- */
+struct i2c_client {
- struct i2c_adapter *adapter;
- unsigned int address;
+};
- #define U_BOOT_I2C_MKENT_COMPLETE(_init, _probe, _read, _write, \ _set_speed, _speed, _slaveaddr, _hwadapnr, _name) \ { \
@@ -271,6 +295,78 @@ unsigned int i2c_get_bus_speed(void);
- Adjusts I2C pointers after U-Boot is relocated to DRAM
*/ void i2c_reloc_fixup(void);
+/*
- i2c_adapter_get() - get the I2C adapter associated with a given index
- @index: index of the I2C adapter
- */
+struct i2c_adapter *i2c_adapter_get(unsigned int index);
+/*
- i2c_adapter_read() - read data from an I2C slave at a given address
- @adapter: I2C adapter
- @chip: address of the I2C slave to read from
- @address: address within the I2C slave to read from
- @alen: length of address
- @buffer: buffer to receive data from I2C slave
- @size: number of bytes to read
- */
+int i2c_adapter_read(struct i2c_adapter *adapter, uint8_t chip,
unsigned int address, size_t alen, void *buffer,
size_t size);
+/*
- i2c_adapter_write() - write data to an I2C slave at a given address
- @adapter: I2C adapter
- @chip: address of the I2C slave to write to
- @address: address within the I2C slave to write to
- @alen: length of address
- @buffer: buffer containing the data to write
- @size: number of bytes to write
- Ideally the function would take a const void * buffer, but the underlying
- infrastructure doesn't properly propagate const and adding it here would
- cause a lot of build warnings.
- */
+int i2c_adapter_write(struct i2c_adapter *adapter, uint8_t chip,
unsigned int address, size_t alen, void *buffer,
size_t size);
+/*
- i2c_client_init() - initialize an I2C slave
- @client: I2C slave
- @adapter: parent I2C adapter
- @address: address of I2C slave
- */
+int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter,
uint8_t address);
+/*
- i2c_client_read() - read data from an I2C slave
- @client: I2C slave
- @address: address within the I2C slave to read from
- @alen: length of address
- @buffer: buffer to receive data from I2C slave
- @size: number of bytes to read
- */
+int i2c_client_read(struct i2c_client *client, unsigned int address,
size_t alen, void *buffer, size_t size);
+/*
- i2c_client_write() - write data to an I2C slave
- @client: I2C slave
- @address: address within the I2C slave to write to
- @alen: length of address
- @buffer: buffer containing the data to write
- @size: number of bytes to write
- Ideally the function would take a const void * buffer, but the underlying
- infrastructure doesn't properly propagate const and adding it here would
- cause a lot of build warnings.
- */
+int i2c_client_write(struct i2c_client *client, unsigned int address,
size_t alen, void *buffer, size_t size);
- #if defined(CONFIG_SYS_I2C_SOFT) void i2c_soft_init(void); void i2c_soft_active(void);

On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot doesn't run multithreaded this works. If you're really concerned about this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with this isn't going to work (in a multithreaded environment the switch to a different mux could happen between the call to i2c_set_bus_num() and the bus access).
In fact I think this would even have to be solved at the controller level if you want to make sure that client transactions are atomic.
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
That's complexity that users shouldn't have to worry about. They should simply access an adapter and the adapter (or rather the core) should take care of setting up any muxes correctly.
Maybe there is on one i2c adapter a i2c mux with 4 ports. On one is an eeprom, on the other is a PMIC ... your code in patch "power: Add AMS AS3722 PMIC support" does access with your functions the PMIC ... what is, if between this accesses someone accesses the eeprom? If he switches the mux, you never switch back!
Your code did not check this!
Like I said, a lot of code in U-Boot doesn't check this. And quite frankly as long as this isn't handled in the core I don't think people will get it right.
Why is i2c_set_bus_num() such a problem?
Because it's completely confusing. And it's exposing an implementation detail to users instead of handling it transparently in the core.
Thierry

Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C) - add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I know, this is a big change and a lot of work ... thats the reason why we are not at this point ... nobody volunteered to go forward, and I did not found time to do it ...
this isn't going to work (in a multithreaded environment the switch to a different mux could happen between the call to i2c_set_bus_num() and the bus access).
In fact I think this would even have to be solved at the controller level if you want to make sure that client transactions are atomic.
As U-Boot is single threaded all i2c_read/writes are atomic.
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS)&& (I2C_ADAP->init_done> 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
That's complexity that users shouldn't have to worry about. They should
Exactly!
simply access an adapter and the adapter (or rather the core) should take care of setting up any muxes correctly.
Yes!
I think you mix here i2c adapter with bus. An "U-Boot i2c adapter" is a hw adapter (or special case soft i2c adapter). An "i2c bus" is a hw adapter maybe with m muxes, and each bus has exactly one way through the i2c muxes, see for an example the README:
http://git.denx.de/?p=u-boot.git;a=blob;f=README;h=14d6b227d689825025f9dfc98...
So the only thing a User must know when he wants to use an i2c bus is his number. The switching to this i2c adapter, initializing it and maybe set i2c muxes does the i2c subsystem ...
Maybe there is on one i2c adapter a i2c mux with 4 ports. On one is an eeprom, on the other is a PMIC ... your code in patch "power: Add AMS AS3722 PMIC support" does access with your functions the PMIC ... what is, if between this accesses someone accesses the eeprom? If he switches the mux, you never switch back!
Your code did not check this!
Like I said, a lot of code in U-Boot doesn't check this. And quite
With using i2c_set_bus_num() you have not to check this! You only have to call i2c_set_bus_num() before calling i2c_read/write ... and yes, that would be nice, if we just pass the bus number to i2c_read/write() and drop the i2c_set_bus_num() call all over the code ...
Patches welcome!
frankly as long as this isn't handled in the core I don't think people will get it right.
Yes, full ack, which is the goal from CONFIG_SYS_I2C. If all i2c driver are converted to it, we can make i2c_set_bus_num() static, and add to the i2c API the bus number as a function parameter!
Why is i2c_set_bus_num() such a problem?
Because it's completely confusing. And it's exposing an implementation detail to users instead of handling it transparently in the core.
Yes! Full Ack ... but I do not accept a new API for that! Please fix the i2c_read/write() functions!
bye, Heiko

On Wed, Aug 27, 2014 at 09:07:58AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C)
- add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I don't think adding a bus number as parameter is useful. Why not just use the I2C adapter directly? That way we don't have to keep looking it up in an array every time.
I know, this is a big change and a lot of work ... thats the reason why we are not at this point ... nobody volunteered to go forward, and I did not found time to do it ...
I suppose that would be one possibility to do it. But I consider i2c_client more of a convenience around the lower-level i2c_read() and i2c_write(). The idea is that users set up an I2C client once and then refer to the client, which will automatically use the correct adapter and slave address rather than having that duplicated in every driver.
this isn't going to work (in a multithreaded environment the switch to a different mux could happen between the call to i2c_set_bus_num() and the bus access).
In fact I think this would even have to be solved at the controller level if you want to make sure that client transactions are atomic.
As U-Boot is single threaded all i2c_read/writes are atomic.
In which case you don't have to call i2c_set_bus_num() for every access, only whenever you don't know exactly where you're coming from. Functions that perform a sequence of accesses only need to set it once.
Also, if we directly talk to an adapter instead, then the bulk of what i2c_set_bus_num() does isn't even required. It would require that adapters are made aware of a hierarchy if there are muxes, but I think that's worthwhile to do in any case. Also if ever I2C muxing needs to gain device tree support having the muxes set up dynamically will be pretty much a prerequisite.
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS)&& (I2C_ADAP->init_done> 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
That's complexity that users shouldn't have to worry about. They should
Exactly!
simply access an adapter and the adapter (or rather the core) should take care of setting up any muxes correctly.
Yes!
I think you mix here i2c adapter with bus. An "U-Boot i2c adapter" is a hw adapter (or special case soft i2c adapter). An "i2c bus" is a hw adapter maybe with m muxes, and each bus has exactly one way through the i2c muxes, see for an example the README:
http://git.denx.de/?p=u-boot.git;a=blob;f=README;h=14d6b227d689825025f9dfc98...
So the only thing a User must know when he wants to use an i2c bus is his number. The switching to this i2c adapter, initializing it and maybe set i2c muxes does the i2c subsystem ...
The above doesn't preclude an I2C adapter representing one of the ports of a mux. That way you can still talk to an adapter rather than having to refer to the bus by number. Adapter would become a little more abstract than it is now, since it would be simply an output that I2C slaves are connected to (either a HW controller directly or a mux connected to a HW controller).
Maybe there is on one i2c adapter a i2c mux with 4 ports. On one is an eeprom, on the other is a PMIC ... your code in patch "power: Add AMS AS3722 PMIC support" does access with your functions the PMIC ... what is, if between this accesses someone accesses the eeprom? If he switches the mux, you never switch back!
Your code did not check this!
Like I said, a lot of code in U-Boot doesn't check this. And quite
With using i2c_set_bus_num() you have not to check this! You only have to call i2c_set_bus_num() before calling i2c_read/write ... and yes, that would be nice, if we just pass the bus number to i2c_read/write() and drop the i2c_set_bus_num() call all over the code ...
Patches welcome!
How about a slightly different proposal: introduce a new level of abstraction (like i2c_client) and start using it in new I2C slave drivers. At the same time existing drivers could be converted one at a time without having the big flag date when i2c_read() and i2c_write() are switched over all at once.
When that new level of abstraction is used, we can hide all the details behind that and the implementation no longer influences any of the drivers. Then we could transparently rework adapters and muxes to our heart's content without needing to update users of the high-level API.
frankly as long as this isn't handled in the core I don't think people will get it right.
Yes, full ack, which is the goal from CONFIG_SYS_I2C. If all i2c driver are converted to it, we can make i2c_set_bus_num() static, and add to the i2c API the bus number as a function parameter!
Why is i2c_set_bus_num() such a problem?
Because it's completely confusing. And it's exposing an implementation detail to users instead of handling it transparently in the core.
Yes! Full Ack ... but I do not accept a new API for that! Please fix the i2c_read/write() functions!
Doing this kind of conversion is a nightmare. We'd be changing an API that has around 600 occurrences in U-Boot, all of which need to be changed *at once* to avoid build breakage. At the same time we need to make sure any patches in development use the same API, which means that they can't even be build-tested until the the API has been changed.
Transitioning step by step is a lot less complicated.
Thierry

Hello Thierry,
Am 27.08.2014 10:51, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 09:07:58AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C)
- add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I don't think adding a bus number as parameter is useful. Why not just use the I2C adapter directly? That way we don't have to keep looking it up in an array every time.
You again just talk from i2c_adapter ... why you ignore i2c muxes? A bus is not only an i2c adapter ...
Currently we have two "versions" of i2c_adapter:
In a system without muxes, you can say: i2c bus == i2c adapter but in a system with muxes we have: i2c bus == i2c_bus_hose
i2c commands use also a "bus number" starting from 0 ... the bus number has historical reasons... we could change this ...
But if we introduce a new API, please with mux functionallity ... Hmm.. thinking about it ... if you want to introduce a new API, please start with using the DM for I2C!
I know, this is a big change and a lot of work ... thats the reason why we are not at this point ... nobody volunteered to go forward, and I did not found time to do it ...
I suppose that would be one possibility to do it. But I consider i2c_client more of a convenience around the lower-level i2c_read() and i2c_write(). The idea is that users set up an I2C client once and then refer to the client, which will automatically use the correct adapter and slave address rather than having that duplicated in every driver.
Hmm... Ok, if we want to have a i2c_client struct instead an int ... What do others think?
But, if we(you ;-) touch this, please start with using the DM for I2C! This is also a step which should be done, and I do not want to have another API, if somedays we find time to switch to DM!
this isn't going to work (in a multithreaded environment the switch to a different mux could happen between the call to i2c_set_bus_num() and the bus access).
In fact I think this would even have to be solved at the controller level if you want to make sure that client transactions are atomic.
As U-Boot is single threaded all i2c_read/writes are atomic.
In which case you don't have to call i2c_set_bus_num() for every access, only whenever you don't know exactly where you're coming from. Functions that perform a sequence of accesses only need to set it once.
Yes ... which really is a pro for having i2c_set_bus_num() not static ...
Also, if we directly talk to an adapter instead, then the bulk of what
You ignore again muxes ...
i2c_set_bus_num() does isn't even required. It would require that
What does i2c_set_bus_num() ?
- first check, if current bus = new bus, and if it is initialized if so -> all is good, return!
Thats the main case ... is this so expensive? This checks should be always done (except we do a bulk of i2c accesses, yes). I think, this has also to be done somewhere with your approach ... or?
What is done in the case, we switch to another bus:
- If we have no muxes, save new bus for further use - look if new bus is initialized, if not initialize it
All this is hidden from the user ... and actions, which must be done, whatever API you define ...
If I understand you correct, your big change is not using "int bus" instead introducing i2c_client ... and define some functions, which use this struct as parameter ...
Why not defining:
[1]: int i2c_bus_read(int bus, uint8_t chip, unsigned int address, size_t alen, void *buffer, size_t size) { i2c_set_bus_num(bus); return i2c_read(chip, address, alen, buffer, size); }
int i2c_bus_write(int bus, uint8_t chip, unsigned int address, size_t alen, void *buffer, size_t size) { i2c_set_bus_num(bus); return i2c_write(chip, address, alen, buffer, size); }
and you never have to call something like i2c_init(), as i2c_set_bus_num() does this all for you! You only have to use in your driver i2c_bus_read/write ... without taking any attention ...
looking deeper in your approach:
+int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter, + uint8_t address) +{ + client->adapter = adapter; + client->address = address; + + return 0; +}
Where is here called adapter->init() ? Nor you check, if the i2c adapter is intialized ... if more than one driver uses this function, each intializes the hw? Or currently none :-(
adapters are made aware of a hierarchy if there are muxes, but I think that's worthwhile to do in any case. Also if ever I2C muxing needs to gain device tree support having the muxes set up dynamically will be pretty much a prerequisite.
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS)&& (I2C_ADAP->init_done> 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
That's complexity that users shouldn't have to worry about. They should
Exactly!
simply access an adapter and the adapter (or rather the core) should take care of setting up any muxes correctly.
Yes!
I think you mix here i2c adapter with bus. An "U-Boot i2c adapter" is a hw adapter (or special case soft i2c adapter). An "i2c bus" is a hw adapter maybe with m muxes, and each bus has exactly one way through the i2c muxes, see for an example the README:
http://git.denx.de/?p=u-boot.git;a=blob;f=README;h=14d6b227d689825025f9dfc98...
So the only thing a User must know when he wants to use an i2c bus is his number. The switching to this i2c adapter, initializing it and maybe set i2c muxes does the i2c subsystem ...
The above doesn't preclude an I2C adapter representing one of the ports of a mux. That way you can still talk to an adapter rather than having to refer to the bus by number. Adapter would become a little more abstract than it is now, since it would be simply an output that I2C
Oh!
slaves are connected to (either a HW controller directly or a mux connected to a HW controller).
Thats sounds for me, that you should use DM ... I do not know, what your plans are!
Maybe there is on one i2c adapter a i2c mux with 4 ports. On one is an eeprom, on the other is a PMIC ... your code in patch "power: Add AMS AS3722 PMIC support" does access with your functions the PMIC ... what is, if between this accesses someone accesses the eeprom? If he switches the mux, you never switch back!
Your code did not check this!
Like I said, a lot of code in U-Boot doesn't check this. And quite
With using i2c_set_bus_num() you have not to check this! You only have to call i2c_set_bus_num() before calling i2c_read/write ... and yes, that would be nice, if we just pass the bus number to i2c_read/write() and drop the i2c_set_bus_num() call all over the code ...
Patches welcome!
How about a slightly different proposal: introduce a new level of abstraction (like i2c_client) and start using it in new I2C slave drivers. At the same time existing drivers could be converted one at a time without having the big flag date when i2c_read() and i2c_write() are switched over all at once.
Sound like use/introduce DM for I2C!
When that new level of abstraction is used, we can hide all the details behind that and the implementation no longer influences any of the drivers. Then we could transparently rework adapters and muxes to our heart's content without needing to update users of the high-level API.
Ok, with some functions like in [1], maybe you introduce i2c_client, and use this instead "int bus" ...
frankly as long as this isn't handled in the core I don't think people will get it right.
Yes, full ack, which is the goal from CONFIG_SYS_I2C. If all i2c driver are converted to it, we can make i2c_set_bus_num() static, and add to the i2c API the bus number as a function parameter!
Why is i2c_set_bus_num() such a problem?
Because it's completely confusing. And it's exposing an implementation detail to users instead of handling it transparently in the core.
Yes! Full Ack ... but I do not accept a new API for that! Please fix the i2c_read/write() functions!
Doing this kind of conversion is a nightmare. We'd be changing an API
Full Ack.
that has around 600 occurrences in U-Boot, all of which need to be changed *at once* to avoid build breakage. At the same time we need to make sure any patches in development use the same API, which means that they can't even be build-tested until the the API has been changed.
Transitioning step by step is a lot less complicated.
Ok, it was worth a try ;-)
So what do you think about defining functions like in [1] ?
bye, Heiko

On Wed, Aug 27, 2014 at 11:56:41AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 10:51, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 09:07:58AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
that refers to a particular slave connected to an adapter). This is useful to avoid having to call i2c_set_bus_num() whenever a device is being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C)
- add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I don't think adding a bus number as parameter is useful. Why not just use the I2C adapter directly? That way we don't have to keep looking it up in an array every time.
You again just talk from i2c_adapter ... why you ignore i2c muxes? A bus is not only an i2c adapter ...
I know. I keep saying i2c_adapter because in the rough sketch that I have in mind for how this could work eventually, an mux is just another special kind of i2c_adapter.
Currently we have two "versions" of i2c_adapter:
In a system without muxes, you can say: i2c bus == i2c adapter but in a system with muxes we have: i2c bus == i2c_bus_hose
i2c commands use also a "bus number" starting from 0 ... the bus number has historical reasons... we could change this ...
But if we introduce a new API, please with mux functionallity ... Hmm.. thinking about it ... if you want to introduce a new API, please start with using the DM for I2C!
I can look into it, but it sounds like a task way beyond what I have time for right now.
this isn't going to work (in a multithreaded environment the switch to a different mux could happen between the call to i2c_set_bus_num() and the bus access).
In fact I think this would even have to be solved at the controller level if you want to make sure that client transactions are atomic.
As U-Boot is single threaded all i2c_read/writes are atomic.
In which case you don't have to call i2c_set_bus_num() for every access, only whenever you don't know exactly where you're coming from. Functions that perform a sequence of accesses only need to set it once.
Yes ... which really is a pro for having i2c_set_bus_num() not static ...
Or if said functionality is encapsulated within adapters then they can be done as necessary with the same effect.
Also, if we directly talk to an adapter instead, then the bulk of what
You ignore again muxes ...
i2c_set_bus_num() does isn't even required. It would require that
What does i2c_set_bus_num() ?
- first check, if current bus = new bus, and if it is initialized if so -> all is good, return!
Thats the main case ... is this so expensive? This checks should be always done (except we do a bulk of i2c accesses, yes). I think, this has also to be done somewhere with your approach ... or?
It's currently done as part of i2c_adapter_get() which as you already point out only work when muxes aren't used. But the main issue about the current approach is that we have these global indices and pass them around in global state and need to be careful to always set the current bus where appropriate.
If we simply pass adapters around (provided they handle muxes correctly) we can simply address each slave as an (adapter, slave address) pair, rather than repeating the same arguments over and over and calling very low-level bus management functions.
But we already agree that this isn't optimal. The disagreement seems to be mostly about the implementation details.
What is done in the case, we switch to another bus:
- If we have no muxes, save new bus for further use
- look if new bus is initialized, if not initialize it
All this is hidden from the user ... and actions, which must be done, whatever API you define ...
If I understand you correct, your big change is not using "int bus" instead introducing i2c_client ... and define some functions, which use this struct as parameter ...
Correct.
Why not defining:
[1]: int i2c_bus_read(int bus, uint8_t chip, unsigned int address, size_t alen, void *buffer, size_t size) { i2c_set_bus_num(bus); return i2c_read(chip, address, alen, buffer, size); }
int i2c_bus_write(int bus, uint8_t chip, unsigned int address, size_t alen, void *buffer, size_t size) { i2c_set_bus_num(bus); return i2c_write(chip, address, alen, buffer, size); }
and you never have to call something like i2c_init(), as i2c_set_bus_num() does this all for you! You only have to use in your driver i2c_bus_read/write ... without taking any attention ...
Yes, that sounds like a useful change in itself. But it still requires users to explicitly manage the bus and chip numbers. The goal of the i2c_client API is to standardize on how to talk to clients. Essentially the goal would be for a driver to get a hold of a struct i2c_client via some standard interface (perhaps with DM this could be done completely outside of the driver itself) and then talks to the device via this standard object. Currently drivers either need to hardcode the bus number and slave addresses, or they need to store them in some custom structure. i2c_client gives uniformity to that.
There are really two parts of a puzzle here. One is the client-side API that drivers for slave devices use and the other is the I2C controller drivers that provide the struct i2c_adapter.
looking deeper in your approach:
+int i2c_client_init(struct i2c_client *client, struct i2c_adapter *adapter,
uint8_t address)
+{
- client->adapter = adapter;
- client->address = address;
- return 0;
+}
Where is here called adapter->init() ? Nor you check, if the i2c adapter is intialized ... if more than one driver uses this function, each intializes the hw? Or currently none :-(
It's done as part of the i2c_adapter_get(). It takes an index (which is roughly the bus number) initializes the corresponding adapter (if found) and returns a pointer to it.
To handle muxes I think this could work by adding hooks that could be called before doing transactions to make sure that the correct muxing is set up.
adapters are made aware of a hierarchy if there are muxes, but I think that's worthwhile to do in any case. Also if ever I2C muxing needs to gain device tree support having the muxes set up dynamically will be pretty much a prerequisite.
This is collected in i2c_set_bus_num() ... before, every "user" did this on his own ... if you are on the bus you want to access, the overhead is not so big, see:
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/i2c/i2c_core.c;h=18d673660...
278 int i2c_set_bus_num(unsigned int bus) 279 { 280 int max; 281 282 if ((bus == I2C_BUS)&& (I2C_ADAP->init_done> 0)) 283 return 0;
And you must be aware of i2c muxes! You directly use the read/write functions from the i2c adapter, but what is if you have i2c muxes?
That's complexity that users shouldn't have to worry about. They should
Exactly!
simply access an adapter and the adapter (or rather the core) should take care of setting up any muxes correctly.
Yes!
I think you mix here i2c adapter with bus. An "U-Boot i2c adapter" is a hw adapter (or special case soft i2c adapter). An "i2c bus" is a hw adapter maybe with m muxes, and each bus has exactly one way through the i2c muxes, see for an example the README:
http://git.denx.de/?p=u-boot.git;a=blob;f=README;h=14d6b227d689825025f9dfc98...
So the only thing a User must know when he wants to use an i2c bus is his number. The switching to this i2c adapter, initializing it and maybe set i2c muxes does the i2c subsystem ...
The above doesn't preclude an I2C adapter representing one of the ports of a mux. That way you can still talk to an adapter rather than having to refer to the bus by number. Adapter would become a little more abstract than it is now, since it would be simply an output that I2C
Oh!
slaves are connected to (either a HW controller directly or a mux connected to a HW controller).
Thats sounds for me, that you should use DM ... I do not know, what your plans are!
Yes, perhaps DM is part of the solution. But I don't have any concrete plans. I'd rather tackle this step by step rather than as one monolithic patch series.
When that new level of abstraction is used, we can hide all the details behind that and the implementation no longer influences any of the drivers. Then we could transparently rework adapters and muxes to our heart's content without needing to update users of the high-level API.
Ok, with some functions like in [1], maybe you introduce i2c_client, and use this instead "int bus" ...
This would probably work fine to approach the problem from two sides. An API like the i2c_client could be used to abstract away all the implementation details on the controller/mux side and then more work could be done to convert the adapter side of things to whatever works best.
Thierry

Hi Thierry,
On 27 August 2014 05:41, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Aug 27, 2014 at 11:56:41AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 10:51, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 09:07:58AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding: >From: Thierry Redingtreding@nvidia.com > >This API operates on I2C adapters or I2C clients (a new type of object
which is a bad idea ...
>that refers to a particular slave connected to an adapter). This is >useful to avoid having to call i2c_set_bus_num() whenever a device is >being accessed.
But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, you must check before every access, if you are on it, if not, you must switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C)
- add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I don't think adding a bus number as parameter is useful. Why not just use the I2C adapter directly? That way we don't have to keep looking it up in an array every time.
You again just talk from i2c_adapter ... why you ignore i2c muxes? A bus is not only an i2c adapter ...
I know. I keep saying i2c_adapter because in the rough sketch that I have in mind for how this could work eventually, an mux is just another special kind of i2c_adapter.
Currently we have two "versions" of i2c_adapter:
In a system without muxes, you can say: i2c bus == i2c adapter but in a system with muxes we have: i2c bus == i2c_bus_hose
i2c commands use also a "bus number" starting from 0 ... the bus number has historical reasons... we could change this ...
But if we introduce a new API, please with mux functionallity ... Hmm.. thinking about it ... if you want to introduce a new API, please start with using the DM for I2C!
I can look into it, but it sounds like a task way beyond what I have time for right now.
I can help if you are interested. I have patches for SPI at u-boot-dm.git (branch working) and would like to have I2C. They are sort-of similar so it might not be to hard to convert Tegra I2C over.
The concept of a 'current' bus is broken IMO. Maybe the command line should have this concept, and maintain a 'current hose' or whatever. But drives and other code should specific what they need with each transaction. With DM that will likely be automatic.
Regards, Simon

Hello Simon,
Am 27.08.2014 21:10, schrieb Simon Glass:
Hi Thierry,
On 27 August 2014 05:41, Thierry Redingthierry.reding@gmail.com wrote:
On Wed, Aug 27, 2014 at 11:56:41AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 10:51, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 09:07:58AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 27.08.2014 08:21, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:21:51AM +0200, Heiko Schocher wrote: > Hello Thierry, > > Am 26.08.2014 17:34, schrieb Thierry Reding: >> From: Thierry Redingtreding@nvidia.com >> >> This API operates on I2C adapters or I2C clients (a new type of object > > which is a bad idea ... > >> that refers to a particular slave connected to an adapter). This is >> useful to avoid having to call i2c_set_bus_num() whenever a device is >> being accessed. > > But thats the supose of i2c_set_bus_num()! ... if you use an i2c bus, > you must check before every access, if you are on it, if not, you must > switch back to it...
That's not what code does today. Everybody calls i2c_set_bus_num() once and then does a bunch of transactions on that bus. Given that U-Boot
Yes, sadly. This has historical reasons ...
doesn't run multithreaded this works. If you're really concerned about
Yes, U-Boot is singlethread only.
this being a problem, then it should be solved at a different level. It could be part of i2c_client for example, so that i2c_client_read() and i2c_client_write() would always perform this step. Burdening users with
Exactly, thats right, and this is a goal from the CONFIG_SYS_I2C API!
But why do you introduce i2c_client_read/write and do not add this step to i2c_read/write?
- convert all i2c drivers, which are not yet converted to CONFIG_SYS_I2C (get also rid od CONFIG_HARD_I2C)
- add busnumber to i2c_read/write API and make i2c_set_bus_num() static ... and fix all i2c_read/write() calls in U-Boot code ...
I don't think adding a bus number as parameter is useful. Why not just use the I2C adapter directly? That way we don't have to keep looking it up in an array every time.
You again just talk from i2c_adapter ... why you ignore i2c muxes? A bus is not only an i2c adapter ...
I know. I keep saying i2c_adapter because in the rough sketch that I have in mind for how this could work eventually, an mux is just another special kind of i2c_adapter.
Currently we have two "versions" of i2c_adapter:
In a system without muxes, you can say: i2c bus == i2c adapter but in a system with muxes we have: i2c bus == i2c_bus_hose
i2c commands use also a "bus number" starting from 0 ... the bus number has historical reasons... we could change this ...
But if we introduce a new API, please with mux functionallity ... Hmm.. thinking about it ... if you want to introduce a new API, please start with using the DM for I2C!
I can look into it, but it sounds like a task way beyond what I have time for right now.
I can help if you are interested. I have patches for SPI at u-boot-dm.git (branch working) and would like to have I2C. They are sort-of similar so it might not be to hard to convert Tegra I2C over.
The concept of a 'current' bus is broken IMO. Maybe the command line should have this concept, and maintain a 'current hose' or whatever. But drives and other code should specific what they need with each transaction. With DM that will likely be automatic.
That sounds great! I vote for using DM.
bye, Heiko

From: Thierry Reding treding@nvidia.com
This is useful to retrieve the U-Boot bus number of an I2C controller given a device tree node.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/i2c/tegra_i2c.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 257b72f0f7cd..94453765369d 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -21,6 +21,7 @@ DECLARE_GLOBAL_DATA_PTR;
/* Information about i2c controller */ struct i2c_bus { + int node; int id; enum periph_id periph_id; int speed; @@ -400,6 +401,7 @@ static int process_nodes(const void *blob, int node_list[], int count, continue;
i2c_bus = &i2c_controllers[i]; + i2c_bus->node = node; i2c_bus->id = i;
if (i2c_get_config(blob, node, i2c_bus)) { @@ -624,6 +626,17 @@ int tegra_i2c_get_dvc_bus_num(void) return -1; }
+int i2c_get_bus_num_fdt(int node) +{ + unsigned int i; + + for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) + if (node == i2c_controllers[i].node) + return i; + + return -1; +} + /* * Register soft i2c adapters */

On 26 August 2014 09:34, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
This is useful to retrieve the U-Boot bus number of an I2C controller given a device tree node.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org

From: Thierry Reding treding@nvidia.com
The AS3722 provides a number of DC/DC converters and LDOs as well as 8 GPIOs.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/power/Makefile | 1 + drivers/power/as3722.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++++ include/fdtdec.h | 1 + include/power/as3722.h | 27 +++++ lib/fdtdec.c | 1 + 5 files changed, 330 insertions(+) create mode 100644 drivers/power/as3722.c create mode 100644 include/power/as3722.h
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dc64e4d32bff..b3097316e27e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ #
+obj-$(CONFIG_AS3722_POWER) += as3722.o obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c new file mode 100644 index 000000000000..59d1bf1b50b0 --- /dev/null +++ b/drivers/power/as3722.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define pr_fmt(fmt) "as3722: " fmt + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> + +#include <power/as3722.h> + +#define AS3722_SD_VOLTAGE(n) (0x00 + (n)) +#define AS3722_GPIO_CONTROL(n) (0x08 + (n)) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH (1 << 0) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL (7 << 0) +#define AS3722_GPIO_CONTROL_INVERT (1 << 7) +#define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_SD_CONTROL 0x4d +#define AS3722_LDO_CONTROL 0x4e +#define AS3722_ASIC_ID1 0x90 +#define AS3722_DEVICE_ID 0x0c +#define AS3722_ASIC_ID2 0x91 + +struct as3722 { + struct i2c_client client; + u8 address; +}; + +static struct as3722 as3722_pmic; + +static int as3722_read(struct as3722 *pmic, u8 reg, u8 *value) +{ + int err; + + err = i2c_client_read(&pmic->client, reg, 1, value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_write(struct as3722 *pmic, u8 reg, u8 value) +{ + int err; + + err = i2c_client_write(&pmic->client, reg, 1, &value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_read_id(struct as3722 *pmic, u8 *id, u8 *revision) +{ + int err; + + err = as3722_read(pmic, AS3722_ASIC_ID1, id); + if (err) { + error("failed to read ID1 register: %d", err); + return err; + } + + err = as3722_read(pmic, AS3722_ASIC_ID2, revision); + if (err) { + error("failed to read ID2 register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_enable(struct as3722 *pmic, unsigned int sd) +{ + u8 value; + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_read(pmic, AS3722_SD_CONTROL, &value); + if (err) { + error("failed to read SD control register: %d", err); + return err; + } + + value |= 1 << sd; + + err = as3722_write(pmic, AS3722_SD_CONTROL, value); + if (err < 0) { + error("failed to write SD control register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value) +{ + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_write(pmic, AS3722_SD_VOLTAGE(sd), value); + if (err < 0) { + error("failed to write SD%u voltage register: %d", sd, err); + return err; + } + + return 0; +} + +int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo) +{ + u8 value; + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_read(pmic, AS3722_LDO_CONTROL, &value); + if (err) { + error("failed to read LDO control register: %d", err); + return err; + } + + value |= 1 << ldo; + + err = as3722_write(pmic, AS3722_LDO_CONTROL, value); + if (err < 0) { + error("failed to write LDO control register: %d", err); + return err; + } + + return 0; +} + +int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value) +{ + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_write(pmic, AS3722_LDO_VOLTAGE(ldo), value); + if (err < 0) { + error("failed to write LDO%u voltage register: %d", ldo, + err); + return err; + } + + return 0; +} + +int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio, + unsigned long flags) +{ + u8 value = 0; + int err; + + if (flags & AS3722_GPIO_OUTPUT_VDDH) + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + if (flags & AS3722_GPIO_INVERT) + value |= AS3722_GPIO_CONTROL_INVERT; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u: %d", gpio, err); + return err; + } + + return 0; +} + +static int as3722_gpio_set(struct as3722 *pmic, unsigned int gpio, + unsigned int level) +{ + const char *l; + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + err = as3722_read(pmic, AS3722_GPIO_SIGNAL_OUT, &value); + if (err < 0) { + error("failed to read GPIO signal out register: %d", err); + return err; + } + + if (level == 0) { + value &= ~(1 << gpio); + l = "low"; + } else { + value |= 1 << gpio; + l = "high"; + } + + err = as3722_write(pmic, AS3722_GPIO_SIGNAL_OUT, value); + if (err) { + error("failed to set GPIO#%u %s: %d", gpio, l, err); + return err; + } + + return 0; +} + +int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio, + unsigned int level) +{ + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + if (level == 0) + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; + else + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u as output: %d", gpio, err); + return err; + } + + err = as3722_gpio_set(pmic, gpio, level); + if (err < 0) { + error("failed to set GPIO#%u high: %d", gpio, err); + return err; + } + + return 0; +} + +int as3722_init(struct as3722 **pmicp, const void *fdt) +{ + struct as3722 *pmic = &as3722_pmic; + int count, nodes[1], i; + int err; + + count = fdtdec_find_aliases_for_id(fdt, NULL, COMPAT_AMS_AS3722, + nodes, ARRAY_SIZE(nodes)); + for (i = 0; i < count; i++) { + int parent = fdt_parent_offset(fdt, nodes[i]), bus; + struct i2c_adapter *adapter; + fdt_addr_t address; + u8 id, revision; + + bus = i2c_get_bus_num_fdt(parent); + if (bus < 0) { + error("invalid bus %d", bus); + continue; + } + + address = fdtdec_get_addr(fdt, nodes[i], "reg"); + if (address == FDT_ADDR_T_NONE) { + error("slave address not found"); + continue; + } + + adapter = i2c_adapter_get(bus); + if (!adapter) { + error("I2C adapter for bus %d not found", bus); + continue; + } + + err = i2c_client_init(&pmic->client, adapter, address); + if (err < 0) { + error("failed to initialize I2C slave: %d", err); + continue; + } + + err = as3722_read_id(pmic, &id, &revision); + if (err < 0) { + error("failed to read ID: %d", err); + continue; + } + + if (id != AS3722_DEVICE_ID) { + error("unknown device"); + continue; + } + + debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", + revision, bus, address); + + *pmicp = pmic; + return 0; + } + + return -ENODEV; +} diff --git a/include/fdtdec.h b/include/fdtdec.h index 5c669a5c8e03..3c38375397df 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -115,6 +115,7 @@ enum fdt_compat_id { COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ COMPAT_TI_TPS65090, /* Texas Instrument TPS65090 */ COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */ + COMPAT_AMS_AS3722, /* AMS AS3722 PMIC */
COMPAT_COUNT, }; diff --git a/include/power/as3722.h b/include/power/as3722.h new file mode 100644 index 000000000000..2c2e45a44bab --- /dev/null +++ b/include/power/as3722.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __POWER_AS3722_H__ +#define __POWER_AS3722_H__ + +#include <asm/types.h> + +#define AS3722_GPIO_OUTPUT_VDDH (1 << 0) +#define AS3722_GPIO_INVERT (1 << 1) + +struct as3722; + +int as3722_init(struct as3722 **pmic, const void *fdt); +int as3722_sd_enable(struct as3722 *pmic, unsigned int sd); +int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value); +int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo); +int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value); +int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio, + unsigned long flags); +int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio, + unsigned int level); + +#endif /* __POWER_AS3722_H__ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index fa5da0c0147d..cddd86c3186f 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -70,6 +70,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), COMPAT(TI_TPS65090, "ti,tps65090"), COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"), + COMPAT(AMS_AS3722, "ams,as3722"), };
const char *fdtdec_get_compatible(enum fdt_compat_id id)

Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
From: Thierry Redingtreding@nvidia.com
The AS3722 provides a number of DC/DC converters and LDOs as well as 8 GPIOs.
Signed-off-by: Thierry Redingtreding@nvidia.com
drivers/power/Makefile | 1 + drivers/power/as3722.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++++ include/fdtdec.h | 1 + include/power/as3722.h | 27 +++++ lib/fdtdec.c | 1 + 5 files changed, 330 insertions(+) create mode 100644 drivers/power/as3722.c create mode 100644 include/power/as3722.h
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dc64e4d32bff..b3097316e27e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ #
+obj-$(CONFIG_AS3722_POWER) += as3722.o obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c new file mode 100644 index 000000000000..59d1bf1b50b0 --- /dev/null +++ b/drivers/power/as3722.c @@ -0,0 +1,300 @@ +/*
- Copyright (C) 2014 NVIDIA Corporation
- SPDX-License-Identifier: GPL-2.0+
- */
+#define pr_fmt(fmt) "as3722: " fmt
+#include<common.h> +#include<errno.h> +#include<fdtdec.h> +#include<i2c.h>
+#include<power/as3722.h>
+#define AS3722_SD_VOLTAGE(n) (0x00 + (n)) +#define AS3722_GPIO_CONTROL(n) (0x08 + (n)) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH (1<< 0) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL (7<< 0) +#define AS3722_GPIO_CONTROL_INVERT (1<< 7) +#define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_SD_CONTROL 0x4d +#define AS3722_LDO_CONTROL 0x4e +#define AS3722_ASIC_ID1 0x90 +#define AS3722_DEVICE_ID 0x0c +#define AS3722_ASIC_ID2 0x91
+struct as3722 {
- struct i2c_client client;
- u8 address;
+};
+static struct as3722 as3722_pmic;
+static int as3722_read(struct as3722 *pmic, u8 reg, u8 *value) +{
- int err;
- err = i2c_client_read(&pmic->client, reg, 1, value, 1);
- if (err< 0)
return err;
- return 0;
+}
+static int as3722_write(struct as3722 *pmic, u8 reg, u8 value) +{
- int err;
- err = i2c_client_write(&pmic->client, reg, 1,&value, 1);
- if (err< 0)
return err;
- return 0;
+}
+static int as3722_read_id(struct as3722 *pmic, u8 *id, u8 *revision) +{
- int err;
- err = as3722_read(pmic, AS3722_ASIC_ID1, id);
- if (err) {
error("failed to read ID1 register: %d", err);
return err;
- }
- err = as3722_read(pmic, AS3722_ASIC_ID2, revision);
- if (err) {
error("failed to read ID2 register: %d", err);
return err;
- }
- return 0;
+}
+int as3722_sd_enable(struct as3722 *pmic, unsigned int sd) +{
- u8 value;
- int err;
- if (sd> 6)
return -EINVAL;
- err = as3722_read(pmic, AS3722_SD_CONTROL,&value);
- if (err) {
error("failed to read SD control register: %d", err);
return err;
- }
- value |= 1<< sd;
- err = as3722_write(pmic, AS3722_SD_CONTROL, value);
- if (err< 0) {
error("failed to write SD control register: %d", err);
return err;
- }
- return 0;
+}
+int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value) +{
- int err;
- if (sd> 6)
return -EINVAL;
- err = as3722_write(pmic, AS3722_SD_VOLTAGE(sd), value);
- if (err< 0) {
error("failed to write SD%u voltage register: %d", sd, err);
return err;
- }
- return 0;
+}
+int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo) +{
- u8 value;
- int err;
- if (ldo> 11)
return -EINVAL;
- err = as3722_read(pmic, AS3722_LDO_CONTROL,&value);
- if (err) {
error("failed to read LDO control register: %d", err);
return err;
- }
- value |= 1<< ldo;
- err = as3722_write(pmic, AS3722_LDO_CONTROL, value);
- if (err< 0) {
error("failed to write LDO control register: %d", err);
return err;
- }
- return 0;
+}
+int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value) +{
- int err;
- if (ldo> 11)
return -EINVAL;
- err = as3722_write(pmic, AS3722_LDO_VOLTAGE(ldo), value);
- if (err< 0) {
error("failed to write LDO%u voltage register: %d", ldo,
err);
return err;
- }
- return 0;
+}
+int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio,
unsigned long flags)
+{
- u8 value = 0;
- int err;
- if (flags& AS3722_GPIO_OUTPUT_VDDH)
value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH;
- if (flags& AS3722_GPIO_INVERT)
value |= AS3722_GPIO_CONTROL_INVERT;
- err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value);
- if (err) {
error("failed to configure GPIO#%u: %d", gpio, err);
return err;
- }
- return 0;
+}
+static int as3722_gpio_set(struct as3722 *pmic, unsigned int gpio,
unsigned int level)
+{
- const char *l;
- u8 value;
- int err;
- if (gpio> 7)
return -EINVAL;
- err = as3722_read(pmic, AS3722_GPIO_SIGNAL_OUT,&value);
- if (err< 0) {
error("failed to read GPIO signal out register: %d", err);
return err;
- }
- if (level == 0) {
value&= ~(1<< gpio);
l = "low";
- } else {
value |= 1<< gpio;
l = "high";
- }
- err = as3722_write(pmic, AS3722_GPIO_SIGNAL_OUT, value);
- if (err) {
error("failed to set GPIO#%u %s: %d", gpio, l, err);
return err;
- }
- return 0;
+}
+int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio,
unsigned int level)
+{
- u8 value;
- int err;
- if (gpio> 7)
return -EINVAL;
- if (level == 0)
value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL;
- else
value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH;
- err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value);
- if (err) {
error("failed to configure GPIO#%u as output: %d", gpio, err);
return err;
- }
- err = as3722_gpio_set(pmic, gpio, level);
- if (err< 0) {
error("failed to set GPIO#%u high: %d", gpio, err);
return err;
- }
- return 0;
+}
+int as3722_init(struct as3722 **pmicp, const void *fdt) +{
- struct as3722 *pmic =&as3722_pmic;
- int count, nodes[1], i;
- int err;
- count = fdtdec_find_aliases_for_id(fdt, NULL, COMPAT_AMS_AS3722,
nodes, ARRAY_SIZE(nodes));
- for (i = 0; i< count; i++) {
int parent = fdt_parent_offset(fdt, nodes[i]), bus;
struct i2c_adapter *adapter;
fdt_addr_t address;
u8 id, revision;
bus = i2c_get_bus_num_fdt(parent);
if (bus< 0) {
error("invalid bus %d", bus);
continue;
}
address = fdtdec_get_addr(fdt, nodes[i], "reg");
if (address == FDT_ADDR_T_NONE) {
error("slave address not found");
continue;
}
adapter = i2c_adapter_get(bus);
if (!adapter) {
error("I2C adapter for bus %d not found", bus);
continue;
}
Why is i2c_set_bus_num() not enough for you? Why introducing a new API? If another uses this PMIC behind a i2c mux, this driver maybe not work correctly.
bye, Heiko
err = i2c_client_init(&pmic->client, adapter, address);
if (err< 0) {
error("failed to initialize I2C slave: %d", err);
continue;
}
err = as3722_read_id(pmic,&id,&revision);
if (err< 0) {
error("failed to read ID: %d", err);
continue;
}
if (id != AS3722_DEVICE_ID) {
error("unknown device");
continue;
}
debug("AS3722 revision %#x found on I2C bus %u, address %#x\n",
revision, bus, address);
*pmicp = pmic;
return 0;
- }
- return -ENODEV;
+} diff --git a/include/fdtdec.h b/include/fdtdec.h index 5c669a5c8e03..3c38375397df 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -115,6 +115,7 @@ enum fdt_compat_id { COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ COMPAT_TI_TPS65090, /* Texas Instrument TPS65090 */ COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */
COMPAT_AMS_AS3722, /* AMS AS3722 PMIC */
COMPAT_COUNT, };
diff --git a/include/power/as3722.h b/include/power/as3722.h new file mode 100644 index 000000000000..2c2e45a44bab --- /dev/null +++ b/include/power/as3722.h @@ -0,0 +1,27 @@ +/*
- Copyright (C) 2014 NVIDIA Corporation
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __POWER_AS3722_H__ +#define __POWER_AS3722_H__
+#include<asm/types.h>
+#define AS3722_GPIO_OUTPUT_VDDH (1<< 0) +#define AS3722_GPIO_INVERT (1<< 1)
+struct as3722;
+int as3722_init(struct as3722 **pmic, const void *fdt); +int as3722_sd_enable(struct as3722 *pmic, unsigned int sd); +int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value); +int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo); +int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value); +int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio,
unsigned long flags);
+int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio,
unsigned int level);
+#endif /* __POWER_AS3722_H__ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index fa5da0c0147d..cddd86c3186f 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -70,6 +70,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), COMPAT(TI_TPS65090, "ti,tps65090"), COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"),
COMPAT(AMS_AS3722, "ams,as3722"), };
const char *fdtdec_get_compatible(enum fdt_compat_id id)

On Wed, Aug 27, 2014 at 07:26:12AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
[...]
+int as3722_init(struct as3722 **pmicp, const void *fdt) +{
- struct as3722 *pmic =&as3722_pmic;
- int count, nodes[1], i;
- int err;
- count = fdtdec_find_aliases_for_id(fdt, NULL, COMPAT_AMS_AS3722,
nodes, ARRAY_SIZE(nodes));
- for (i = 0; i< count; i++) {
int parent = fdt_parent_offset(fdt, nodes[i]), bus;
struct i2c_adapter *adapter;
fdt_addr_t address;
u8 id, revision;
bus = i2c_get_bus_num_fdt(parent);
if (bus< 0) {
error("invalid bus %d", bus);
continue;
}
address = fdtdec_get_addr(fdt, nodes[i], "reg");
if (address == FDT_ADDR_T_NONE) {
error("slave address not found");
continue;
}
adapter = i2c_adapter_get(bus);
if (!adapter) {
error("I2C adapter for bus %d not found", bus);
continue;
}
Why is i2c_set_bus_num() not enough for you? Why introducing a new API? If another uses this PMIC behind a i2c mux, this driver maybe not work correctly.
As I explained earlier, I think requiring users to manually micro-manage the I2C busses is a bad idea. It's very tedious and error prone. It does also mean that every patch needs to be carefully checked that the I2C API is used correctly. The intent of the i2c_client API was to introduce a higher-level object that people can use and that does the bulk of the work behind the scenes rather than duplicating it all over the place. It is true that perhaps it currently doesn't work well with I2C muxes. But the advantage is that when it does, it does so for everybody that uses it.
Thierry

Hello Thierry,
Am 27.08.2014 08:28, schrieb Thierry Reding:
On Wed, Aug 27, 2014 at 07:26:12AM +0200, Heiko Schocher wrote:
Hello Thierry,
Am 26.08.2014 17:34, schrieb Thierry Reding:
[...]
+int as3722_init(struct as3722 **pmicp, const void *fdt) +{
- struct as3722 *pmic =&as3722_pmic;
- int count, nodes[1], i;
- int err;
- count = fdtdec_find_aliases_for_id(fdt, NULL, COMPAT_AMS_AS3722,
nodes, ARRAY_SIZE(nodes));
- for (i = 0; i< count; i++) {
int parent = fdt_parent_offset(fdt, nodes[i]), bus;
struct i2c_adapter *adapter;
fdt_addr_t address;
u8 id, revision;
bus = i2c_get_bus_num_fdt(parent);
if (bus< 0) {
error("invalid bus %d", bus);
continue;
}
address = fdtdec_get_addr(fdt, nodes[i], "reg");
if (address == FDT_ADDR_T_NONE) {
error("slave address not found");
continue;
}
adapter = i2c_adapter_get(bus);
if (!adapter) {
error("I2C adapter for bus %d not found", bus);
continue;
}
Why is i2c_set_bus_num() not enough for you? Why introducing a new API? If another uses this PMIC behind a i2c mux, this driver maybe not work correctly.
As I explained earlier, I think requiring users to manually micro-manage the I2C busses is a bad idea. It's very tedious and error prone. It does
I fully aggree with you.
also mean that every patch needs to be carefully checked that the I2C API is used correctly. The intent of the i2c_client API was to introduce a higher-level object that people can use and that does the bulk of the work behind the scenes rather than duplicating it all over the place. It
I understand you, but why not fixing the current API, so all can provide from your work?
is true that perhaps it currently doesn't work well with I2C muxes. But the advantage is that when it does, it does so for everybody that uses it.
Please fix the i2c_read/write functions, and it works for free with i2c muxes!
On my ToDo list:
- Fix all old style i2c driver - make i2c_set_bus_num() static, and add to i2c_read/write() the new function parameter "bus" ... - get rid of HARD_I2C - remove all i2c_init() calls, as they no longer needed - remove all oldstyle defines, functions, structs ... - introduce a deinit() functions for i2c adapters, so if we switch to another i2c bus, we deactivate the current i2c hw adapter. So if we boot an OS, we have all i2c hw adapters deactivated.
bye, Heiko

From: Thierry Reding treding@nvidia.com
This function is required by PCIe and SATA. This patch implements it on Tegra20, Tegra30 and Tegra124. It isn't implemented for Tegra114 because it doesn't support PCIe or SATA.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/cpu/tegra124-common/clock.c | 109 ++++++++++++++++++++ arch/arm/cpu/tegra20-common/clock.c | 137 +++++++++++++++++++++++++ arch/arm/cpu/tegra30-common/clock.c | 154 +++++++++++++++++++++++++++++ arch/arm/include/asm/arch-tegra124/clock.h | 2 + arch/arm/include/asm/arch-tegra20/clock.h | 2 + arch/arm/include/asm/arch-tegra30/clock.h | 2 + 6 files changed, 406 insertions(+)
diff --git a/arch/arm/cpu/tegra124-common/clock.c b/arch/arm/cpu/tegra124-common/clock.c index 739436326eca..fc8bd194ddc9 100644 --- a/arch/arm/cpu/tegra124-common/clock.c +++ b/arch/arm/cpu/tegra124-common/clock.c @@ -824,3 +824,112 @@ void arch_timer_init(void) writel(val, &sysctr->cntcr); debug("%s: TSC CNTCR = 0x%08X\n", __func__, val); } + +#define PLLE_SS_CNTL 0x68 +#define PLLE_SS_CNTL_SSCINCINTR(x) (((x) & 0x3f) << 24) +#define PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16) +#define PLLE_SS_CNTL_SSCINVERT (1 << 15) +#define PLLE_SS_CNTL_SSCCENTER (1 << 14) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0) + +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_ENABLE (1 << 30) +#define PLLE_BASE_LOCK_OVERRIDE (1 << 29) +#define PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24) +#define PLLE_BASE_NDIV(x) (((x) & 0xff) << 8) +#define PLLE_BASE_MDIV(x) (((x) & 0xff) << 0) + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_IDDQ_SWCTL (1 << 14) +#define PLLE_MISC_IDDQ_OVERRIDE (1 << 13) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_PTS (1 << 8) +#define PLLE_MISC_VREG_BG_CTRL(x) (((x) & 0x3) << 4) +#define PLLE_MISC_VREG_CTRL(x) (((x) & 0x3) << 2) + +#define PLLE_AUX 0x48c +#define PLLE_AUX_SEQ_ENABLE (1 << 24) +#define PLLE_AUX_ENABLE_SWCTL (1 << 4) + +int tegra_plle_enable(void) +{ + unsigned int m = 1, n = 200, cpcon = 13; + u32 value; + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value &= ~PLLE_BASE_LOCK_OVERRIDE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_AUX); + value |= PLLE_AUX_ENABLE_SWCTL; + value &= ~PLLE_AUX_SEQ_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX); + + udelay(1); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value |= PLLE_MISC_IDDQ_SWCTL; + value &= ~PLLE_MISC_IDDQ_OVERRIDE; + value |= PLLE_MISC_LOCK_ENABLE; + value |= PLLE_MISC_PTS; + value |= PLLE_MISC_VREG_BG_CTRL(3); + value |= PLLE_MISC_VREG_CTRL(2); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + udelay(5); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET | + PLLE_SS_CNTL_BYPASS_SS; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value &= ~PLLE_BASE_PLDIV_CML(0xf); + value &= ~PLLE_BASE_NDIV(0xff); + value &= ~PLLE_BASE_MDIV(0xff); + value |= PLLE_BASE_PLDIV_CML(cpcon); + value |= PLLE_BASE_NDIV(n); + value |= PLLE_BASE_MDIV(m); + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + udelay(1); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value |= PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + /* wait for lock */ + udelay(300); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_SSCINVERT; + value &= ~PLLE_SS_CNTL_SSCCENTER; + + value &= ~PLLE_SS_CNTL_SSCINCINTR(0x3f); + value &= ~PLLE_SS_CNTL_SSCINC(0xff); + value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff); + + value |= PLLE_SS_CNTL_SSCINCINTR(0x20); + value |= PLLE_SS_CNTL_SSCINC(0x01); + value |= PLLE_SS_CNTL_SSCMAX(0x25); + + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_SSCBYP; + value &= ~PLLE_SS_CNTL_BYPASS_SS; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + udelay(1); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_INTERP_RESET; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + udelay(1); + + return 0; +} diff --git a/arch/arm/cpu/tegra20-common/clock.c b/arch/arm/cpu/tegra20-common/clock.c index 0c4f5fb288a0..d55fbc87c340 100644 --- a/arch/arm/cpu/tegra20-common/clock.c +++ b/arch/arm/cpu/tegra20-common/clock.c @@ -7,6 +7,7 @@ /* Tegra20 Clock control functions */
#include <common.h> +#include <errno.h> #include <asm/io.h> #include <asm/arch/clock.h> #include <asm/arch/tegra.h> @@ -548,3 +549,139 @@ void clock_early_init(void) void arch_timer_init(void) { } + +#define PMC_SATA_PWRGT 0x1ac +#define PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE (1 << 5) +#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL (1 << 4) + +#define PLLE_SS_CNTL 0x68 +#define PLLE_SS_CNTL_SSCINCINTRV(x) (((x) & 0x3f) << 24) +#define PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0) + +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_ENABLE_CML (1 << 31) +#define PLLE_BASE_ENABLE (1 << 30) +#define PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24) +#define PLLE_BASE_PLDIV(x) (((x) & 0x3f) << 16) +#define PLLE_BASE_NDIV(x) (((x) & 0xff) << 8) +#define PLLE_BASE_MDIV(x) (((x) & 0xff) << 0) + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_SETUP_BASE(x) (((x) & 0xffff) << 16) +#define PLLE_MISC_PLL_READY (1 << 15) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_SETUP_EXT(x) (((x) & 0x3) << 2) + +static int tegra_plle_train(void) +{ + unsigned int timeout = 2000; + unsigned long value; + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value &= ~PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_PLL_READY) + break; + + udelay(100); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to become ready"); + return -ETIMEDOUT; + } + + return 0; +} + +int tegra_plle_enable(void) +{ + unsigned int timeout = 1000; + u32 value; + int err; + + /* disable PLLE clock */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value &= ~PLLE_BASE_ENABLE_CML; + value &= ~PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + /* clear lock enable and setup field */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value &= ~PLLE_MISC_LOCK_ENABLE; + value &= ~PLLE_MISC_SETUP_BASE(0xffff); + value &= ~PLLE_MISC_SETUP_EXT(0x3); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if ((value & PLLE_MISC_PLL_READY) == 0) { + err = tegra_plle_train(); + if (err < 0) { + error("failed to train PLLE: %d", err); + return err; + } + } + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value |= PLLE_MISC_SETUP_BASE(0x7); + value |= PLLE_MISC_LOCK_ENABLE; + value |= PLLE_MISC_SETUP_EXT(0); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET | + PLLE_SS_CNTL_BYPASS_SS; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value |= PLLE_BASE_ENABLE_CML | PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_LOCK) + break; + + udelay(2); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to lock"); + return -ETIMEDOUT; + } + + udelay(50); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_SSCINCINTRV(0x3f); + value |= PLLE_SS_CNTL_SSCINCINTRV(0x18); + + value &= ~PLLE_SS_CNTL_SSCINC(0xff); + value |= PLLE_SS_CNTL_SSCINC(0x01); + + value &= ~PLLE_SS_CNTL_SSCBYP; + value &= ~PLLE_SS_CNTL_INTERP_RESET; + value &= ~PLLE_SS_CNTL_BYPASS_SS; + + value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff); + value |= PLLE_SS_CNTL_SSCMAX(0x24); + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + return 0; +} diff --git a/arch/arm/cpu/tegra30-common/clock.c b/arch/arm/cpu/tegra30-common/clock.c index 80ba2d8c1ca5..8e5c49888219 100644 --- a/arch/arm/cpu/tegra30-common/clock.c +++ b/arch/arm/cpu/tegra30-common/clock.c @@ -17,6 +17,7 @@ /* Tegra30 Clock control functions */
#include <common.h> +#include <errno.h> #include <asm/io.h> #include <asm/arch/clock.h> #include <asm/arch/tegra.h> @@ -587,3 +588,156 @@ void clock_early_init(void) void arch_timer_init(void) { } + +#define PMC_SATA_PWRGT 0x1ac +#define PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE (1 << 5) +#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL (1 << 4) + +#define PLLE_SS_CNTL 0x68 +#define PLLE_SS_CNTL_SSCINCINTRV(x) (((x) & 0x3f) << 24) +#define PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0) + +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_ENABLE_CML (1 << 31) +#define PLLE_BASE_ENABLE (1 << 30) +#define PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24) +#define PLLE_BASE_PLDIV(x) (((x) & 0x3f) << 16) +#define PLLE_BASE_NDIV(x) (((x) & 0xff) << 8) +#define PLLE_BASE_MDIV(x) (((x) & 0xff) << 0) + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_SETUP_BASE(x) (((x) & 0xffff) << 16) +#define PLLE_MISC_PLL_READY (1 << 15) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_SETUP_EXT(x) (((x) & 0x3) << 2) + +static int tegra_plle_train(void) +{ + unsigned int timeout = 2000; + unsigned long value; + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value &= ~PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_PLL_READY) + break; + + udelay(100); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to become ready"); + return -ETIMEDOUT; + } + + return 0; +} + +int tegra_plle_enable(void) +{ + unsigned int cpcon = 11, p = 18, n = 150, m = 1, timeout = 1000; + u32 value; + int err; + + /* disable PLLE clock */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value &= ~PLLE_BASE_ENABLE_CML; + value &= ~PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + /* clear lock enable and setup field */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value &= ~PLLE_MISC_LOCK_ENABLE; + value &= ~PLLE_MISC_SETUP_BASE(0xffff); + value &= ~PLLE_MISC_SETUP_EXT(0x3); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if ((value & PLLE_MISC_PLL_READY) == 0) { + err = tegra_plle_train(); + if (err < 0) { + error("failed to train PLLE: %d", err); + return err; + } + } + + /* configure PLLE */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + + value &= ~PLLE_BASE_PLDIV_CML(0x0f); + value |= PLLE_BASE_PLDIV_CML(cpcon); + + value &= ~PLLE_BASE_PLDIV(0x3f); + value |= PLLE_BASE_PLDIV(p); + + value &= ~PLLE_BASE_NDIV(0xff); + value |= PLLE_BASE_NDIV(n); + + value &= ~PLLE_BASE_MDIV(0xff); + value |= PLLE_BASE_MDIV(m); + + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value |= PLLE_MISC_SETUP_BASE(0x7); + value |= PLLE_MISC_LOCK_ENABLE; + value |= PLLE_MISC_SETUP_EXT(0); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET | + PLLE_SS_CNTL_BYPASS_SS; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value |= PLLE_BASE_ENABLE_CML | PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_LOCK) + break; + + udelay(2); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to lock"); + return -ETIMEDOUT; + } + + udelay(50); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_SSCINCINTRV(0x3f); + value |= PLLE_SS_CNTL_SSCINCINTRV(0x18); + + value &= ~PLLE_SS_CNTL_SSCINC(0xff); + value |= PLLE_SS_CNTL_SSCINC(0x01); + + value &= ~PLLE_SS_CNTL_SSCBYP; + value &= ~PLLE_SS_CNTL_INTERP_RESET; + value &= ~PLLE_SS_CNTL_BYPASS_SS; + + value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff); + value |= PLLE_SS_CNTL_SSCMAX(0x24); + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + return 0; +} diff --git a/arch/arm/include/asm/arch-tegra124/clock.h b/arch/arm/include/asm/arch-tegra124/clock.h index 8e39d21a7b5e..8e650862529e 100644 --- a/arch/arm/include/asm/arch-tegra124/clock.h +++ b/arch/arm/include/asm/arch-tegra124/clock.h @@ -16,4 +16,6 @@ #define OSC_FREQ_SHIFT 28 #define OSC_FREQ_MASK (0xF << OSC_FREQ_SHIFT)
+int tegra_plle_enable(void); + #endif /* _TEGRA124_CLOCK_H_ */ diff --git a/arch/arm/include/asm/arch-tegra20/clock.h b/arch/arm/include/asm/arch-tegra20/clock.h index 889c65a16f1f..4df8da96e2a3 100644 --- a/arch/arm/include/asm/arch-tegra20/clock.h +++ b/arch/arm/include/asm/arch-tegra20/clock.h @@ -15,4 +15,6 @@ #define OSC_FREQ_SHIFT 30 #define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT)
+int tegra_plle_enable(void); + #endif /* _TEGRA20_CLOCK_H */ diff --git a/arch/arm/include/asm/arch-tegra30/clock.h b/arch/arm/include/asm/arch-tegra30/clock.h index 2f24a75cc4c3..410c35289978 100644 --- a/arch/arm/include/asm/arch-tegra30/clock.h +++ b/arch/arm/include/asm/arch-tegra30/clock.h @@ -25,4 +25,6 @@ #define OSC_FREQ_SHIFT 28 #define OSC_FREQ_MASK (0xF << OSC_FREQ_SHIFT)
+int tegra_plle_enable(void); + #endif /* _TEGRA30_CLOCK_H_ */

From: Thierry Reding treding@nvidia.com
This reset is required for PCIe and the corresponding ID therefore needs to be defined. The enumeration value for this was properly defined on some SoCs but not on others. Similarly, some contained it in the mapping of peripheral IDs to clock IDs, other didn't. This patch defines it consistently for all supported SoC generations.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/cpu/tegra20-common/clock.c | 4 ++-- arch/arm/cpu/tegra30-common/clock.c | 1 + arch/arm/include/asm/arch-tegra20/clock-tables.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/arm/cpu/tegra20-common/clock.c b/arch/arm/cpu/tegra20-common/clock.c index d55fbc87c340..7b9e10cd93ae 100644 --- a/arch/arm/cpu/tegra20-common/clock.c +++ b/arch/arm/cpu/tegra20-common/clock.c @@ -333,7 +333,7 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { /* 0x48 */ NONE(AFI), NONE(CORESIGHT), - NONE(RESERVED74), + NONE(PCIEXCLK), NONE(AVPUCQ), NONE(RESERVED76), NONE(RESERVED77), @@ -495,7 +495,7 @@ enum periph_id clk_id_to_periph_id(int clk_id) case PERIPH_ID_RESERVED30: case PERIPH_ID_RESERVED35: case PERIPH_ID_RESERVED56: - case PERIPH_ID_RESERVED74: + case PERIPH_ID_PCIEXCLK: case PERIPH_ID_RESERVED76: case PERIPH_ID_RESERVED77: case PERIPH_ID_RESERVED78: diff --git a/arch/arm/cpu/tegra30-common/clock.c b/arch/arm/cpu/tegra30-common/clock.c index 8e5c49888219..0eb0f0ade37c 100644 --- a/arch/arm/cpu/tegra30-common/clock.c +++ b/arch/arm/cpu/tegra30-common/clock.c @@ -564,6 +564,7 @@ enum periph_id clk_id_to_periph_id(int clk_id) case PERIPH_ID_RESERVED43: case PERIPH_ID_RESERVED45: case PERIPH_ID_RESERVED56: + case PERIPH_ID_PCIEXCLK: case PERIPH_ID_RESERVED76: case PERIPH_ID_RESERVED77: case PERIPH_ID_RESERVED78: diff --git a/arch/arm/include/asm/arch-tegra20/clock-tables.h b/arch/arm/include/asm/arch-tegra20/clock-tables.h index a09cb0197863..894be088cde2 100644 --- a/arch/arm/include/asm/arch-tegra20/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra20/clock-tables.h @@ -131,7 +131,7 @@ enum periph_id { /* 72 */ PERIPH_ID_AFI, PERIPH_ID_CORESIGHT, - PERIPH_ID_RESERVED74, + PERIPH_ID_PCIEXCLK, PERIPH_ID_AVPUCQ, PERIPH_ID_RESERVED76, PERIPH_ID_RESERVED77,

From: Thierry Reding treding@nvidia.com
Implement the powergate API that allows various power partitions to be power up and down.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - wait for power-up/off operation to complete before returning - document PCIE/VDEC mixup in REMOVE_CLAMPING register - add symbolic name for REMOVE_CLAMPING register - list all partitions available on existing SoCs
arch/arm/cpu/tegra-common/Makefile | 1 + arch/arm/cpu/tegra-common/powergate.c | 102 +++++++++++++++++++++++++ arch/arm/include/asm/arch-tegra/powergate.h | 38 +++++++++ arch/arm/include/asm/arch-tegra114/powergate.h | 6 ++ arch/arm/include/asm/arch-tegra124/powergate.h | 6 ++ arch/arm/include/asm/arch-tegra20/powergate.h | 6 ++ arch/arm/include/asm/arch-tegra30/powergate.h | 6 ++ 7 files changed, 165 insertions(+) create mode 100644 arch/arm/cpu/tegra-common/powergate.c create mode 100644 arch/arm/include/asm/arch-tegra/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra114/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra124/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra20/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra30/powergate.h
diff --git a/arch/arm/cpu/tegra-common/Makefile b/arch/arm/cpu/tegra-common/Makefile index 892556e64451..fed618ba8fc9 100644 --- a/arch/arm/cpu/tegra-common/Makefile +++ b/arch/arm/cpu/tegra-common/Makefile @@ -13,4 +13,5 @@ obj-y += cache.o obj-y += clock.o obj-y += lowlevel_init.o obj-y += pinmux-common.o +obj-y += powergate.o obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o diff --git a/arch/arm/cpu/tegra-common/powergate.c b/arch/arm/cpu/tegra-common/powergate.c new file mode 100644 index 000000000000..439cff36b951 --- /dev/null +++ b/arch/arm/cpu/tegra-common/powergate.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> + +#include <asm/io.h> +#include <asm/types.h> + +#include <asm/arch/powergate.h> +#include <asm/arch/tegra.h> + +#define PWRGATE_TOGGLE 0x30 +#define PWRGATE_TOGGLE_START (1 << 8) + +#define REMOVE_CLAMPING 0x34 + +#define PWRGATE_STATUS 0x38 + +static int tegra_powergate_set(enum tegra_powergate id, bool state) +{ + u32 value, mask = state ? (1 << id) : 0, old_mask; + unsigned long start, timeout = 25; + + value = readl(NV_PA_PMC_BASE + PWRGATE_STATUS); + old_mask = value & (1 << id); + + if (mask == old_mask) + return 0; + + writel(PWRGATE_TOGGLE_START | id, NV_PA_PMC_BASE + PWRGATE_TOGGLE); + + start = get_timer(0); + + while (get_timer(start) < timeout) { + value = readl(NV_PA_PMC_BASE + PWRGATE_STATUS); + if ((value & (1 << id)) == mask) + return 0; + } + + return -ETIMEDOUT; +} + +static int tegra_powergate_power_on(enum tegra_powergate id) +{ + return tegra_powergate_set(id, true); +} + +int tegra_powergate_power_off(enum tegra_powergate id) +{ + return tegra_powergate_set(id, false); +} + +static int tegra_powergate_remove_clamping(enum tegra_powergate id) +{ + unsigned long value; + + /* + * The REMOVE_CLAMPING register has the bits for the PCIE and VDEC + * partitions reversed. This was originally introduced on Tegra20 but + * has since been carried forward for backwards-compatibility. + */ + if (id == TEGRA_POWERGATE_VDEC) + value = 1 << TEGRA_POWERGATE_PCIE; + else if (id == TEGRA_POWERGATE_PCIE) + value = 1 << TEGRA_POWERGATE_VDEC; + else + value = 1 << id; + + writel(value, NV_PA_PMC_BASE + REMOVE_CLAMPING); + + return 0; +} + +int tegra_powergate_sequence_power_up(enum tegra_powergate id, + enum periph_id periph) +{ + int err; + + reset_set_enable(periph, 1); + + err = tegra_powergate_power_on(id); + if (err < 0) + return err; + + clock_enable(periph); + + udelay(10); + + err = tegra_powergate_remove_clamping(id); + if (err < 0) + return err; + + udelay(10); + + reset_set_enable(periph, 0); + + return 0; +} diff --git a/arch/arm/include/asm/arch-tegra/powergate.h b/arch/arm/include/asm/arch-tegra/powergate.h new file mode 100644 index 000000000000..130b58bef1b4 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/powergate.h @@ -0,0 +1,38 @@ +#ifndef _TEGRA_POWERGATE_H_ +#define _TEGRA_POWERGATE_H_ + +#include <asm/arch/clock.h> + +enum tegra_powergate { + TEGRA_POWERGATE_CPU, + TEGRA_POWERGATE_3D, + TEGRA_POWERGATE_VENC, + TEGRA_POWERGATE_PCIE, + TEGRA_POWERGATE_VDEC, + TEGRA_POWERGATE_L2, + TEGRA_POWERGATE_MPE, + TEGRA_POWERGATE_HEG, + TEGRA_POWERGATE_SATA, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, + TEGRA_POWERGATE_CELP, + TEGRA_POWERGATE_3D1, + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_C0NC, + TEGRA_POWERGATE_C1NC, + TEGRA_POWERGATE_SOR, + TEGRA_POWERGATE_DIS, + TEGRA_POWERGATE_DISB, + TEGRA_POWERGATE_XUSBA, + TEGRA_POWERGATE_XUSBB, + TEGRA_POWERGATE_XUSBC, + TEGRA_POWERGATE_VIC, + TEGRA_POWERGATE_IRAM, +}; + +int tegra_powergate_sequence_power_up(enum tegra_powergate id, + enum periph_id periph); +int tegra_powergate_power_off(enum tegra_powergate id); + +#endif diff --git a/arch/arm/include/asm/arch-tegra114/powergate.h b/arch/arm/include/asm/arch-tegra114/powergate.h new file mode 100644 index 000000000000..260ea801b81b --- /dev/null +++ b/arch/arm/include/asm/arch-tegra114/powergate.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA114_POWERGATE_H_ +#define _TEGRA114_POWERGATE_H_ + +#include <asm/arch-tegra/powergate.h> + +#endif /* _TEGRA114_POWERGATE_H_ */ diff --git a/arch/arm/include/asm/arch-tegra124/powergate.h b/arch/arm/include/asm/arch-tegra124/powergate.h new file mode 100644 index 000000000000..8a0cfbaf9665 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra124/powergate.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA124_POWERGATE_H_ +#define _TEGRA124_POWERGATE_H_ + +#include <asm/arch-tegra/powergate.h> + +#endif /* _TEGRA124_POWERGATE_H_ */ diff --git a/arch/arm/include/asm/arch-tegra20/powergate.h b/arch/arm/include/asm/arch-tegra20/powergate.h new file mode 100644 index 000000000000..439d88b7022a --- /dev/null +++ b/arch/arm/include/asm/arch-tegra20/powergate.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA20_POWERGATE_H_ +#define _TEGRA20_POWERGATE_H_ + +#include <asm/arch-tegra/powergate.h> + +#endif /* _TEGRA20_POWERGATE_H_ */ diff --git a/arch/arm/include/asm/arch-tegra30/powergate.h b/arch/arm/include/asm/arch-tegra30/powergate.h new file mode 100644 index 000000000000..c70e44b6216d --- /dev/null +++ b/arch/arm/include/asm/arch-tegra30/powergate.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA30_POWERGATE_H_ +#define _TEGRA30_POWERGATE_H_ + +#include <asm/arch-tegra/powergate.h> + +#endif /* _TEGRA30_POWERGATE_H_ */

From: Thierry Reding treding@nvidia.com
This controller was introduced on Tegra114 to handle XUSB pads. On Tegra124 it is also used for PCIe and SATA pin muxing and PHY control. Only the Tegra124 PCIe and SATA functionality is currently implemented, with weak symbols on Tegra114.
Tegra20 and Tegra30 also provide weak symbols for these functions so that drivers can use the same API irrespective of which SoC they're being built for.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - remove needless header files - move more helpers to libfdt - describe PHY type
arch/arm/cpu/tegra-common/Makefile | 1 + arch/arm/cpu/tegra-common/xusb-padctl.c | 39 ++ arch/arm/cpu/tegra124-common/Makefile | 1 + arch/arm/cpu/tegra124-common/xusb-padctl.c | 716 +++++++++++++++++++++++ arch/arm/include/asm/arch-tegra/xusb-padctl.h | 24 + board/nvidia/common/board.c | 3 + include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 + include/fdtdec.h | 2 + lib/fdtdec.c | 1 + 9 files changed, 794 insertions(+) create mode 100644 arch/arm/cpu/tegra-common/xusb-padctl.c create mode 100644 arch/arm/cpu/tegra124-common/xusb-padctl.c create mode 100644 arch/arm/include/asm/arch-tegra/xusb-padctl.h create mode 100644 include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
diff --git a/arch/arm/cpu/tegra-common/Makefile b/arch/arm/cpu/tegra-common/Makefile index fed618ba8fc9..528ffe0a2fcc 100644 --- a/arch/arm/cpu/tegra-common/Makefile +++ b/arch/arm/cpu/tegra-common/Makefile @@ -14,4 +14,5 @@ obj-y += clock.o obj-y += lowlevel_init.o obj-y += pinmux-common.o obj-y += powergate.o +obj-y += xusb-padctl.o obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o diff --git a/arch/arm/cpu/tegra-common/xusb-padctl.c b/arch/arm/cpu/tegra-common/xusb-padctl.c new file mode 100644 index 000000000000..65f8d2ea967a --- /dev/null +++ b/arch/arm/cpu/tegra-common/xusb-padctl.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> + +#include <asm/arch-tegra/xusb-padctl.h> + +struct tegra_xusb_phy * __weak tegra_xusb_phy_get(unsigned int type) +{ + return NULL; +} + +int __weak tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +void __weak tegra_xusb_padctl_init(const void *fdt) +{ +} diff --git a/arch/arm/cpu/tegra124-common/Makefile b/arch/arm/cpu/tegra124-common/Makefile index ff77992b330c..7b59fb121614 100644 --- a/arch/arm/cpu/tegra124-common/Makefile +++ b/arch/arm/cpu/tegra124-common/Makefile @@ -8,3 +8,4 @@ obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o +obj-y += xusb-padctl.o diff --git a/arch/arm/cpu/tegra124-common/xusb-padctl.c b/arch/arm/cpu/tegra124-common/xusb-padctl.c new file mode 100644 index 000000000000..43af883f2c12 --- /dev/null +++ b/arch/arm/cpu/tegra124-common/xusb-padctl.c @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#define pr_fmt(fmt) "tegra-xusb-padctl: " fmt + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> + +#include <asm/io.h> + +#include <asm/arch/clock.h> +#include <asm/arch-tegra/xusb-padctl.h> + +#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> + +#define XUSB_PADCTL_ELPG_PROGRAM 0x01c +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26) +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25) +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4) + +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138 +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27) +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24) +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3) +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1) +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0) + +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1) +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0) + +enum tegra124_function { + TEGRA124_FUNC_SNPS, + TEGRA124_FUNC_XUSB, + TEGRA124_FUNC_UART, + TEGRA124_FUNC_PCIE, + TEGRA124_FUNC_USB3, + TEGRA124_FUNC_SATA, + TEGRA124_FUNC_RSVD, +}; + +static const char *const tegra124_functions[] = { + "snps", + "xusb", + "uart", + "pcie", + "usb3", + "sata", + "rsvd", +}; + +static const unsigned int tegra124_otg_functions[] = { + TEGRA124_FUNC_SNPS, + TEGRA124_FUNC_XUSB, + TEGRA124_FUNC_UART, + TEGRA124_FUNC_RSVD, +}; + +static const unsigned int tegra124_usb_functions[] = { + TEGRA124_FUNC_SNPS, + TEGRA124_FUNC_XUSB, +}; + +static const unsigned int tegra124_pci_functions[] = { + TEGRA124_FUNC_PCIE, + TEGRA124_FUNC_USB3, + TEGRA124_FUNC_SATA, + TEGRA124_FUNC_RSVD, +}; + +struct tegra_xusb_padctl_lane { + const char *name; + + unsigned int offset; + unsigned int shift; + unsigned int mask; + unsigned int iddq; + + const unsigned int *funcs; + unsigned int num_funcs; +}; + +#define TEGRA124_LANE(_name, _offset, _shift, _mask, _iddq, _funcs) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .iddq = _iddq, \ + .num_funcs = ARRAY_SIZE(tegra124_##_funcs##_functions), \ + .funcs = tegra124_##_funcs##_functions, \ + } + +static const struct tegra_xusb_padctl_lane tegra124_lanes[] = { + TEGRA124_LANE("otg-0", 0x004, 0, 0x3, 0, otg), + TEGRA124_LANE("otg-1", 0x004, 2, 0x3, 0, otg), + TEGRA124_LANE("otg-2", 0x004, 4, 0x3, 0, otg), + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, 0, usb), + TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, 0, usb), + TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, 0, usb), + TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, 1, pci), + TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, 2, pci), + TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, 3, pci), + TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, 4, pci), + TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, 5, pci), + TEGRA124_LANE("sata-0", 0x134, 26, 0x3, 6, pci), +}; + +struct tegra_xusb_phy_ops { + int (*prepare)(struct tegra_xusb_phy *phy); + int (*enable)(struct tegra_xusb_phy *phy); + int (*disable)(struct tegra_xusb_phy *phy); + int (*unprepare)(struct tegra_xusb_phy *phy); +}; + +struct tegra_xusb_phy { + const struct tegra_xusb_phy_ops *ops; + + struct tegra_xusb_padctl *padctl; +}; + +struct tegra_xusb_padctl_pin { + const struct tegra_xusb_padctl_lane *lane; + + unsigned int func; + int iddq; +}; + +#define MAX_GROUPS 3 +#define MAX_PINS 6 + +struct tegra_xusb_padctl_group { + const char *name; + + const char *pins[MAX_PINS]; + unsigned int num_pins; + + const char *func; + int iddq; +}; + +struct tegra_xusb_padctl_config { + const char *name; + + struct tegra_xusb_padctl_group groups[MAX_GROUPS]; + unsigned int num_groups; +}; + +struct tegra_xusb_padctl { + struct fdt_resource regs; + + unsigned int enable; + + struct tegra_xusb_phy phys[2]; + + const struct tegra_xusb_padctl_lane *lanes; + unsigned int num_lanes; + + const char *const *functions; + unsigned int num_functions; + + struct tegra_xusb_padctl_config config; +}; + +static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, + unsigned long offset) +{ + return readl(padctl->regs.start + offset); +} + +static inline void padctl_writel(struct tegra_xusb_padctl *padctl, + u32 value, unsigned long offset) +{ + writel(value, padctl->regs.start + offset); +} + +static int tegra_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + if (padctl->enable++ > 0) + return 0; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + udelay(100); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + udelay(100); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + return 0; +} + +static int tegra_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + if (padctl->enable == 0) { + error("tegra-xusb-padctl: unbalanced enable/disable"); + return 0; + } + + if (--padctl->enable > 0) + return 0; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + udelay(100); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + udelay(100); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + return 0; +} + +static int phy_prepare(struct tegra_xusb_phy *phy) +{ + return tegra_xusb_padctl_enable(phy->padctl); +} + +static int phy_unprepare(struct tegra_xusb_phy *phy) +{ + return tegra_xusb_padctl_disable(phy->padctl); +} + +static int pcie_phy_enable(struct tegra_xusb_phy *phy) +{ + struct tegra_xusb_padctl *padctl = phy->padctl; + int err = -ETIMEDOUT; + unsigned long start; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN | + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN | + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + start = get_timer(0); + + while (get_timer(start) < 50) { + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + if (value & XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) { + err = 0; + break; + } + } + + return err; +} + +static int pcie_phy_disable(struct tegra_xusb_phy *phy) +{ + struct tegra_xusb_padctl *padctl = phy->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + return 0; +} + +static int sata_phy_enable(struct tegra_xusb_phy *phy) +{ + struct tegra_xusb_padctl *padctl = phy->padctl; + int err = -ETIMEDOUT; + unsigned long start; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + start = get_timer(0); + + while (get_timer(start) < 50) { + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + if (value & XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) { + err = 0; + break; + } + } + + return err; +} + +static int sata_phy_disable(struct tegra_xusb_phy *phy) +{ + struct tegra_xusb_padctl *padctl = phy->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + + return 0; +} + +static const struct tegra_xusb_phy_ops pcie_phy_ops = { + .prepare = phy_prepare, + .enable = pcie_phy_enable, + .disable = pcie_phy_disable, + .unprepare = phy_unprepare, +}; + +static const struct tegra_xusb_phy_ops sata_phy_ops = { + .prepare = phy_prepare, + .enable = sata_phy_enable, + .disable = sata_phy_disable, + .unprepare = phy_unprepare, +}; + +static struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { + .phys = { + [0] = { + .ops = &pcie_phy_ops, + }, + [1] = { + .ops = &sata_phy_ops, + }, + }, +}; + +static const struct tegra_xusb_padctl_lane * +tegra_xusb_padctl_find_lane(struct tegra_xusb_padctl *padctl, const char *name) +{ + unsigned int i; + + for (i = 0; i < padctl->num_lanes; i++) + if (strcmp(name, padctl->lanes[i].name) == 0) + return &padctl->lanes[i]; + + return NULL; +} + +static int +tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_group *group, + const void *fdt, int node) +{ + unsigned int i; + int len, err; + + group->name = fdt_get_name(fdt, node, &len); + + len = fdt_count_strings(fdt, node, "nvidia,lanes"); + if (len < 0) { + error("tegra-xusb-padctl: failed to parse "nvidia,lanes" property"); + return -EINVAL; + } + + group->num_pins = len; + + for (i = 0; i < group->num_pins; i++) { + err = fdt_get_string_index(fdt, node, "nvidia,lanes", i, + &group->pins[i]); + if (err < 0) { + error("tegra-xusb-padctl: failed to read string from "nvidia,lanes" property"); + return -EINVAL; + } + } + + group->num_pins = len; + + err = fdt_get_string(fdt, node, "nvidia,function", &group->func); + if (err < 0) { + error("tegra-xusb-padctl: failed to parse "nvidia,func" property"); + return -EINVAL; + } + + group->iddq = fdtdec_get_int(fdt, node, "nvidia,iddq", -1); + + return 0; +} + +static int tegra_xusb_padctl_find_function(struct tegra_xusb_padctl *padctl, + const char *name) +{ + unsigned int i; + + for (i = 0; i < padctl->num_functions; i++) + if (strcmp(name, padctl->functions[i]) == 0) + return i; + + return -ENOENT; +} + +static int +tegra_xusb_padctl_lane_find_function(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_padctl_lane *lane, + const char *name) +{ + unsigned int i; + int func; + + func = tegra_xusb_padctl_find_function(padctl, name); + if (func < 0) + return func; + + for (i = 0; i < lane->num_funcs; i++) + if (lane->funcs[i] == func) + return i; + + return -ENOENT; +} + +static int +tegra_xusb_padctl_group_apply(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_padctl_group *group) +{ + unsigned int i; + + for (i = 0; i < group->num_pins; i++) { + const struct tegra_xusb_padctl_lane *lane; + unsigned int func; + u32 value; + + lane = tegra_xusb_padctl_find_lane(padctl, group->pins[i]); + if (!lane) { + error("tegra-xusb-padctl: no lane for pin %s", + group->pins[i]); + continue; + } + + func = tegra_xusb_padctl_lane_find_function(padctl, lane, + group->func); + if (func < 0) { + error("tegra-xusb-padctl: function %s invalid for lane %s: %d", + group->func, lane->name, func); + continue; + } + + value = padctl_readl(padctl, lane->offset); + + /* set pin function */ + value &= ~(lane->mask << lane->shift); + value |= func << lane->shift; + + /* + * Set IDDQ if supported on the lane and specified in the + * configuration. + */ + if (lane->iddq > 0 && group->iddq >= 0) { + if (group->iddq != 0) + value &= ~(1 << lane->iddq); + else + value |= 1 << lane->iddq; + } + + padctl_writel(padctl, value, lane->offset); + } + + return 0; +} + +static int +tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_config *config) +{ + unsigned int i; + + for (i = 0; i < config->num_groups; i++) { + const struct tegra_xusb_padctl_group *group; + int err; + + group = &config->groups[i]; + + err = tegra_xusb_padctl_group_apply(padctl, group); + if (err < 0) { + error("tegra-xusb-padctl: failed to apply group %s: %d", + group->name, err); + continue; + } + } + + return 0; +} + +static int +tegra_xusb_padctl_config_parse_dt(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_config *config, + const void *fdt, int node) +{ + int subnode; + + config->name = fdt_get_name(fdt, node, NULL); + + fdt_for_each_subnode(fdt, subnode, node) { + struct tegra_xusb_padctl_group *group; + int err; + + group = &config->groups[config->num_groups]; + + err = tegra_xusb_padctl_group_parse_dt(padctl, group, fdt, + subnode); + if (err < 0) { + error("tegra-xusb-padctl: failed to parse group %s", + group->name); + return err; + } + + config->num_groups++; + } + + return 0; +} + +static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, + const void *fdt, int node) +{ + int subnode, err; + + err = fdt_get_resource(fdt, node, "reg", 0, &padctl->regs); + if (err < 0) { + error("tegra-xusb-padctl: registers not found"); + return err; + } + + fdt_for_each_subnode(fdt, subnode, node) { + struct tegra_xusb_padctl_config *config = &padctl->config; + + err = tegra_xusb_padctl_config_parse_dt(padctl, config, fdt, + subnode); + if (err < 0) { + error("tegra-xusb-padctl: failed to parse entry %s: %d", + config->name, err); + continue; + } + } + + return 0; +} + +static int process_nodes(const void *fdt, int nodes[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + enum fdt_compat_id id; + int err; + + if (!fdtdec_get_is_enabled(fdt, nodes[i])) + continue; + + id = fdtdec_lookup(fdt, nodes[i]); + switch (id) { + case COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL: + break; + + default: + error("tegra-xusb-padctl: unsupported compatible: %s", + fdtdec_get_compatible(id)); + continue; + } + + padctl->num_lanes = ARRAY_SIZE(tegra124_lanes); + padctl->lanes = tegra124_lanes; + + padctl->num_functions = ARRAY_SIZE(tegra124_functions); + padctl->functions = tegra124_functions; + + err = tegra_xusb_padctl_parse_dt(padctl, fdt, nodes[i]); + if (err < 0) { + error("tegra-xusb-padctl: failed to parse DT: %d", + err); + continue; + } + + /* deassert XUSB padctl reset */ + reset_set_enable(PERIPH_ID_XUSB_PADCTL, 0); + + err = tegra_xusb_padctl_config_apply(padctl, &padctl->config); + if (err < 0) { + error("tegra-xusb-padctl: failed to apply pinmux: %d", + err); + continue; + } + + /* only a single instance is supported */ + break; + } + + return 0; +} + +struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type) +{ + struct tegra_xusb_phy *phy = NULL; + + switch (type) { + case TEGRA_XUSB_PADCTL_PCIE: + phy = &padctl->phys[0]; + phy->padctl = padctl; + break; + + case TEGRA_XUSB_PADCTL_SATA: + phy = &padctl->phys[1]; + phy->padctl = padctl; + break; + } + + return phy; +} + +int tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->prepare) + return phy->ops->prepare(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->enable) + return phy->ops->enable(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->disable) + return phy->ops->disable(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->unprepare) + return phy->ops->unprepare(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +void tegra_xusb_padctl_init(const void *fdt) +{ + int count, nodes[1]; + + count = fdtdec_find_aliases_for_id(fdt, "padctl", + COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; +} diff --git a/arch/arm/include/asm/arch-tegra/xusb-padctl.h b/arch/arm/include/asm/arch-tegra/xusb-padctl.h new file mode 100644 index 000000000000..b4b4c8ba4d10 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/xusb-padctl.h @@ -0,0 +1,24 @@ +#ifndef _TEGRA_XUSB_PADCTL_H_ +#define _TEGRA_XUSB_PADCTL_H_ + +struct tegra_xusb_phy; + +/** + * tegra_xusb_phy_get() - obtain a reference to a specified padctl PHY + * @type: the type of PHY to obtain + * + * The type of PHY varies between SoC generations. Typically there are XUSB, + * PCIe and SATA PHYs, though not all generations support all of them. The + * value of type can usually be directly parsed from a device tree. + * + * Return: a pointer to the PHY or NULL if no such PHY exists + */ +struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type); + +void tegra_xusb_padctl_init(const void *fdt); +int tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy); +int tegra_xusb_phy_enable(struct tegra_xusb_phy *phy); +int tegra_xusb_phy_disable(struct tegra_xusb_phy *phy); +int tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy); + +#endif diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index d01abcee13c1..04573c980189 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -37,6 +37,7 @@ #include <asm/arch-tegra/tegra_mmc.h> #include <asm/arch-tegra/mmc.h> #endif +#include <asm/arch-tegra/xusb-padctl.h> #include <i2c.h> #include <spi.h> #include "emc.h" @@ -161,6 +162,8 @@ int board_init(void) pin_mux_nand(); #endif
+ tegra_xusb_padctl_init(gd->fdt_blob); + #ifdef CONFIG_TEGRA_LP0 /* save Sdram params to PMC 2, 4, and 24 for WB0 */ warmboot_save_sdram_params(); diff --git a/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h new file mode 100644 index 000000000000..914d56da9324 --- /dev/null +++ b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h @@ -0,0 +1,7 @@ +#ifndef _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H +#define _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H 1 + +#define TEGRA_XUSB_PADCTL_PCIE 0 +#define TEGRA_XUSB_PADCTL_SATA 1 + +#endif /* _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H */ diff --git a/include/fdtdec.h b/include/fdtdec.h index 3c38375397df..364da6c67cd2 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -86,6 +86,8 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ + COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, + /* Tegra124 XUSB pad controller */ COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ COMPAT_SAMSUNG_EXYNOS5_SROMC, /* Exynos5 SROMC */ COMPAT_SAMSUNG_S3C2440_I2C, /* Exynos I2C Controller */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index cddd86c3186f..f4851592687f 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -41,6 +41,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), + COMPAT(NVIDIA_TEGRA124_XUSB_PADCTL, "nvidia,tegra124-xusb-padctl"), COMPAT(SMSC_LAN9215, "smsc,lan9215"), COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"), COMPAT(SAMSUNG_S3C2440_I2C, "samsung,s3c2440-i2c"),

From: Thierry Reding treding@nvidia.com
The XUSB pad controller is used for pinmuxing of the XUSB, PCIe and SATA lanes.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra124.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/arch/arm/dts/tegra124.dtsi b/arch/arm/dts/tegra124.dtsi index 4561c5f83943..d48f1a34ec3c 100644 --- a/arch/arm/dts/tegra124.dtsi +++ b/arch/arm/dts/tegra124.dtsi @@ -1,5 +1,6 @@ #include <dt-bindings/gpio/tegra-gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
#include "skeleton.dtsi"
@@ -192,6 +193,15 @@ clocks = <&tegra_car 105>; };
+ padctl: padctl@7009f000 { + compatible = "nvidia,tegra124-xusb-padctl"; + reg = <0x7009f000 0x1000>; + resets = <&tegra_car 142>; + reset-names = "padctl"; + + #phy-cells = <1>; + }; + sdhci@700b0000 { compatible = "nvidia,tegra124-sdhci"; reg = <0x700b0000 0x200>;

From: Thierry Reding treding@nvidia.com
Add the PCIe and SATA lane configuration to the Jetson TK1 device tree, so that the XUSB pad controller can be appropriately configured.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra124-jetson-tk1.dts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts index 464287e03ecf..f61736f0ef0f 100644 --- a/arch/arm/dts/tegra124-jetson-tk1.dts +++ b/arch/arm/dts/tegra124-jetson-tk1.dts @@ -66,6 +66,32 @@ spi-max-frequency = <25000000>; };
+ padctl@7009f000 { + pinctrl-0 = <&padctl_default>; + pinctrl-names = "default"; + + padctl_default: pinmux { + usb3 { + nvidia,lanes = "pcie-0", "pcie-1"; + nvidia,function = "usb3"; + nvidia,iddq = <0>; + }; + + pcie { + nvidia,lanes = "pcie-2", "pcie-3", + "pcie-4"; + nvidia,function = "pcie"; + nvidia,iddq = <0>; + }; + + sata { + nvidia,lanes = "sata-0"; + nvidia,function = "sata"; + nvidia,iddq = <0>; + }; + }; + }; + sdhci@700b0400 { status = "okay"; cd-gpios = <&gpio 170 1>; /* gpio PV2 */

From: Thierry Reding treding@nvidia.com
Add support for the PCIe controller found on some generations of Tegra. Tegra20 has 2 root ports with a total of 4 lanes, Tegra30 has 3 root ports with a total of 6 lanes and Tegra124 has 2 root ports with a total of 5 lanes.
This is based on the Linux kernel driver, originally submitted upstream by Mike Rapoport.
Signed-off-by: Mike Rapoport mike@compulab.co.il Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - update copyright statement - add Mike's Signed-off-by
drivers/pci/Makefile | 1 + drivers/pci/pci_tegra.c | 1143 +++++++++++++++++++++++++++++++++++++++++++++++ include/fdtdec.h | 3 + lib/fdtdec.c | 3 + 4 files changed, 1150 insertions(+) create mode 100644 drivers/pci/pci_tegra.c
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e73a49861983..8eb2320539f9 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -15,5 +15,6 @@ obj-$(CONFIG_FTPCI100) += pci_ftpci100.o obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o +obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o obj-$(CONFIG_WINBOND_83C553) += w83c553f.o diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c new file mode 100644 index 000000000000..a03ad5ff1fb6 --- /dev/null +++ b/drivers/pci/pci_tegra.c @@ -0,0 +1,1143 @@ +/* + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport mike@compulab.co.il + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Copyright (c) 2013-2014, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#define DEBUG +#define pr_fmt(fmt) "tegra-pcie: " fmt + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <pci.h> + +#include <asm/io.h> +#include <asm/gpio.h> + +#include <asm/arch/clock.h> +#include <asm/arch/powergate.h> +#include <asm/arch-tegra/xusb-padctl.h> + +#include <linux/list.h> + +#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_SM_INTR_ENABLE 0xc4 +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) +#define AFI_INTR_EN_PRSNT_SENSE (1 << 8) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) + +#define AFI_PEXBIAS_CTRL_0 0x168 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL_TEGRA20 0x000000B8 +#define PADS_PLL_CTL_TEGRA30 0x000000B4 +#define PADS_PLL_CTL_RST_B4SM (0x1 << 1) +#define PADS_PLL_CTL_LOCKDET (0x1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0x0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (0x1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (0x2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0x0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_BUF_EN (0x1 << 22) + +#define PADS_REFCLK_CFG0 0x000000C8 +#define PADS_REFCLK_CFG1 0x000000CC + +/* + * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit + * entries, one entry per PCIe port. These field definitions and desired + * values aren't in the TRM, but do come from NVIDIA. + */ +#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */ +#define PADS_REFCLK_CFG_E_TERM_SHIFT 7 +#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ +#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ + +/* Default value provided by HW engineering is 0xfa5c */ +#define PADS_REFCLK_CFG_VALUE \ + ( \ + (0x17 << PADS_REFCLK_CFG_TERM_SHIFT) | \ + (0 << PADS_REFCLK_CFG_E_TERM_SHIFT) | \ + (0xa << PADS_REFCLK_CFG_PREDI_SHIFT) | \ + (0xf << PADS_REFCLK_CFG_DRVI_SHIFT) \ + ) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +struct tegra_pcie; + +struct tegra_pcie_port { + struct tegra_pcie *pcie; + + struct fdt_resource regs; + unsigned int num_lanes; + unsigned int index; + + struct list_head list; +}; + +struct tegra_pcie_soc { + unsigned int num_ports; + unsigned long pads_pll_ctl; + unsigned long tx_ref_sel; + bool has_pex_clkreq_en; + bool has_pex_bias_ctrl; + bool has_cml_clk; + bool has_gen2; +}; + +struct tegra_pcie { + struct pci_controller hose; + + struct fdt_resource pads; + struct fdt_resource afi; + struct fdt_resource cs; + + struct fdt_resource prefetch; + struct fdt_resource mem; + struct fdt_resource io; + + struct list_head ports; + unsigned long xbar; + + const struct tegra_pcie_soc *soc; + struct tegra_xusb_phy *phy; +}; + +static inline struct tegra_pcie *to_tegra_pcie(struct pci_controller *hose) +{ + return container_of(hose, struct tegra_pcie, hose); +} + +static void afi_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->afi.start + offset); +} + +static unsigned long afi_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->afi.start + offset); +} + +static void pads_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->pads.start + offset); +} + +static unsigned long pads_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->pads.start + offset); +} + +static unsigned long rp_readl(struct tegra_pcie_port *port, + unsigned long offset) +{ + return readl(port->regs.start + offset); +} + +static void rp_writel(struct tegra_pcie_port *port, unsigned long value, + unsigned long offset) +{ + writel(value, port->regs.start + offset); +} + +static unsigned long tegra_pcie_conf_offset(pci_dev_t bdf, int where) +{ + return ((where & 0xf00) << 16) | (PCI_BUS(bdf) << 16) | + (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | + (where & 0xfc); +} + +static int tegra_pcie_conf_address(struct tegra_pcie *pcie, pci_dev_t bdf, + int where, unsigned long *address) +{ + unsigned int bus = PCI_BUS(bdf); + + if (bus == 0) { + unsigned int dev = PCI_DEV(bdf); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + if (port->index + 1 == dev) { + *address = port->regs.start + (where & ~3); + return 0; + } + } + } else { + *address = pcie->cs.start + tegra_pcie_conf_offset(bdf, where); + return 0; + } + + return -1; +} + +static int tegra_pcie_read_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 *value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) { + *value = 0xffffffff; + return 1; + } + + *value = readl(address); + + /* fixup root port class */ + if (PCI_BUS(bdf) == 0) { + if (where == PCI_CLASS_REVISION) { + *value &= ~0x00ff0000; + *value |= PCI_CLASS_BRIDGE_PCI << 16; + } + } + + return 0; +} + +static int tegra_pcie_write_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) + return 1; + + writel(value, address); + + return 0; +} + +static int tegra_pcie_port_parse_dt(const void *fdt, int node, + struct tegra_pcie_port *port) +{ + const u32 *addr; + int len; + + addr = fdt_getprop(fdt, node, "assigned-addresses", &len); + if (!addr) { + error("property "assigned-addresses" not found"); + return -FDT_ERR_NOTFOUND; + } + + port->regs.start = fdt32_to_cpu(addr[2]); + port->regs.end = port->regs.start + fdt32_to_cpu(addr[4]); + + return 0; +} + +static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, + unsigned long *xbar) +{ + enum fdt_compat_id id = fdtdec_lookup(fdt, node); + + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + switch (lanes) { + case 0x00000004: + debug("single-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; + return 0; + + case 0x00000202: + debug("dual-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + switch (lanes) { + case 0x00000204: + debug("4x1, 2x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; + return 0; + + case 0x00020202: + debug("2x3 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; + return 0; + + case 0x00010104: + debug("4x1, 1x2 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + switch (lanes) { + case 0x0000104: + debug("4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + debug("2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + break; + + default: + break; + } + + return -FDT_ERR_NOTFOUND; +} + +static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + const u32 *ptr, *end; + int len; + + ptr = fdt_getprop(fdt, node, "ranges", &len); + if (!ptr) { + error("missing "ranges" property"); + return -FDT_ERR_NOTFOUND; + } + + end = ptr + len / 4; + + while (ptr < end) { + struct fdt_resource *res = NULL; + u32 space = fdt32_to_cpu(*ptr); + + switch ((space >> 24) & 0x3) { + case 0x01: + res = &pcie->io; + break; + + case 0x02: /* 32 bit */ + case 0x03: /* 64 bit */ + if (space & (1 << 30)) + res = &pcie->prefetch; + else + res = &pcie->mem; + + break; + } + + if (res) { + res->start = fdt32_to_cpu(ptr[3]); + res->end = res->start + fdt32_to_cpu(ptr[5]); + } + + ptr += 3 + 1 + 2; + } + + debug("PCI regions:\n"); + debug(" I/O: %#x-%#x\n", pcie->io.start, pcie->io.end); + debug(" non-prefetchable memory: %#x-%#x\n", pcie->mem.start, + pcie->mem.end); + debug(" prefetchable memory: %#x-%#x\n", pcie->prefetch.start, + pcie->prefetch.end); + + return 0; +} + +static int tegra_pcie_parse_port_info(const void *fdt, int node, + unsigned int *index, + unsigned int *lanes) +{ + pci_dev_t bdf; + int err; + + err = fdtdec_get_int(fdt, node, "nvidia,num-lanes", 0); + if (err < 0) { + error("failed to parse "nvidia,num-lanes" property"); + return err; + } + + *lanes = err; + + err = fdtdec_pci_get_bdf(fdt, node, &bdf); + if (err < 0) { + error("failed to parse "reg" property"); + return err; + } + + *index = PCI_DEV(bdf) - 1; + + return 0; +} + +static int tegra_pcie_parse_dt(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + int err, subnode; + u32 lanes = 0; + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "pads", + &pcie->pads); + if (err < 0) { + error("resource "pads" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "afi", + &pcie->afi); + if (err < 0) { + error("resource "afi" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "cs", + &pcie->cs); + if (err < 0) { + error("resource "cs" not found"); + return err; + } + + pcie->phy = tegra_xusb_phy_get(TEGRA_XUSB_PADCTL_PCIE); + if (pcie->phy) { + err = tegra_xusb_phy_prepare(pcie->phy); + if (err < 0) { + error("failed to prepare PHY: %d", err); + return err; + } + } + + err = tegra_pcie_parse_dt_ranges(fdt, node, pcie); + if (err < 0) { + error("failed to parse "ranges" property"); + return err; + } + + fdt_for_each_subnode(fdt, subnode, node) { + unsigned int index = 0, num_lanes = 0; + struct tegra_pcie_port *port; + + err = tegra_pcie_parse_port_info(fdt, subnode, &index, + &num_lanes); + if (err < 0) { + error("failed to obtain root port info"); + continue; + } + + lanes |= num_lanes << (index << 3); + + if (!fdtdec_get_is_enabled(fdt, subnode)) + continue; + + port = malloc(sizeof(*port)); + if (!port) + continue; + + memset(port, 0, sizeof(*port)); + port->num_lanes = num_lanes; + port->index = index; + + err = tegra_pcie_port_parse_dt(fdt, subnode, port); + if (err < 0) { + free(port); + continue; + } + + list_add_tail(&port->list, &pcie->ports); + port->pcie = pcie; + } + + err = tegra_pcie_get_xbar_config(fdt, node, lanes, &pcie->xbar); + if (err < 0) { + error("invalid lane configuration"); + return err; + } + + return 0; +} + +int __weak tegra_pcie_board_init(void) +{ + return 0; +} + +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long value; + int err; + + /* reset PCIEXCLK logic, AFI controller and PCIe controller */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 1); + reset_set_enable(PERIPH_ID_AFI, 1); + reset_set_enable(PERIPH_ID_PCIE, 1); + + err = tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + if (err < 0) { + error("failed to power off PCIe partition: %d", err); + return err; + } + + tegra_pcie_board_init(); + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + PERIPH_ID_PCIE); + if (err < 0) { + error("failed to power up PCIe partition: %d", err); + return err; + } + + /* take AFI controller out of reset */ + reset_set_enable(PERIPH_ID_AFI, 0); + + /* enable AFI clock */ + clock_enable(PERIPH_ID_AFI); + + if (soc->has_cml_clk) { + /* enable CML clock */ + value = readl(NV_PA_CLK_RST_BASE + 0x48c); + value |= (1 << 0); + value &= ~(1 << 1); + writel(value, NV_PA_CLK_RST_BASE + 0x48c); + } + + err = tegra_plle_enable(); + if (err < 0) { + error("failed to enable PLLE: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long start = get_timer(0); + u32 value; + + while (get_timer(start) < timeout) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } + + return -ETIMEDOUT; +} + +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + int err; + + /* initialize internal PHY, enable up to 16 PCIe lanes */ + pads_writel(pcie, 0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* + * Set up PHY PLL inputs select PLLE output as refclock, set TX + * ref sel to div10 (not div5). + */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + udelay(20); + + /* take PLL out of reset */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value |= PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* configure the reference clock driver */ + value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16); + pads_writel(pcie, value, PADS_REFCLK_CFG0); + + if (soc->num_ports > 2) + pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1); + + /* wait for the PLL to lock */ + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + error("PLL failed to lock: %d", err); + return err; + } + + /* turn off IDDQ override */ + value = pads_readl(pcie, PADS_CTL); + value &= ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* enable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; + pads_writel(pcie, value, PADS_CTL); + + return 0; +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_pcie_port *port; + u32 value; + int err; + + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + if (soc->has_pex_bias_ctrl) + afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + + value = afi_readl(pcie, AFI_PCIE_CONFIG); + value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + value = afi_readl(pcie, AFI_FUSE); + + if (soc->has_gen2) + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + else + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + + afi_writel(pcie, value, AFI_FUSE); + + if (pcie->phy) + err = tegra_xusb_phy_enable(pcie->phy); + else + err = tegra_pcie_phy_enable(pcie); + + if (err < 0) { + error("failed to power on PHY: %d\n", err); + return err; + } + + /* take the PCIEXCLK logic out of reset */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 0); + + /* finally enable PCIe */ + value = afi_readl(pcie, AFI_CONFIGURATION); + value |= AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, value, AFI_CONFIGURATION); + + /* disable all interrupts */ + afi_writel(pcie, 0, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0, AFI_SM_INTR_ENABLE); + afi_writel(pcie, 0, AFI_INTR_MASK); + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); + + return 0; +} + +static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) +{ + unsigned long fpci, axi, size; + + /* BAR 0: type 1 extended configuration space */ + fpci = 0xfe100000; + size = fdt_resource_size(&pcie->cs); + axi = pcie->cs.start; + + afi_writel(pcie, axi, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR0); + + /* BAR 1: downstream I/O */ + fpci = 0xfdfc0000; + size = fdt_resource_size(&pcie->io); + axi = pcie->io.start; + + afi_writel(pcie, axi, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR1); + + /* BAR 2: prefetchable memory */ + fpci = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->prefetch); + axi = pcie->prefetch.start; + + afi_writel(pcie, axi, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR2); + + /* BAR 3: non-prefetchable memory */ + fpci = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->mem); + axi = pcie->mem.start; + + afi_writel(pcie, axi, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR3); + + /* NULL out the remaining BARs as they are not used */ + afi_writel(pcie, 0, AFI_AXI_BAR4_START); + afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR4); + + afi_writel(pcie, 0, AFI_AXI_BAR5_START); + afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(pcie, NV_PA_SDRAM_BASE, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + + /* MSI translations are setup only when needed */ + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); +} + +static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) +{ + unsigned long ret = 0; + + switch (port->index) { + case 0: + ret = AFI_PEX0_CTRL; + break; + + case 1: + ret = AFI_PEX1_CTRL; + break; + + case 2: + ret = AFI_PEX2_CTRL; + break; + } + + return ret; +} + +static void tegra_pcie_port_reset(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* pulse reset signel */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + udelay(2000); + + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_enable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* enable reference clock */ + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_REFCLK_EN; + + if (port->pcie->soc->has_pex_clkreq_en) + value |= AFI_PEX_CTRL_CLKREQ_EN; + + value |= AFI_PEX_CTRL_OVERRIDE_EN; + + afi_writel(port->pcie, value, ctrl); + + tegra_pcie_port_reset(port); +} + +static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) +{ + unsigned int retries = 3; + unsigned long value; + + value = rp_readl(port, RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + rp_writel(port, value, RP_PRIV_MISC); + + do { + unsigned int timeout = 200; + + do { + value = rp_readl(port, RP_VEND_XP); + if (value & RP_VEND_XP_DL_UP) + break; + + udelay(2000); + } while (--timeout); + + if (!timeout) { + debug("link %u down, retrying\n", port->index); + goto retry; + } + + timeout = 200; + + do { + value = rp_readl(port, RP_LINK_CONTROL_STATUS); + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + return true; + + udelay(2000); + } while (--timeout); + +retry: + tegra_pcie_port_reset(port); + } while (--retries); + + return false; +} + +static void tegra_pcie_port_disable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* assert port reset */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + /* disable reference clock */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_free(struct tegra_pcie_port *port) +{ + list_del(&port->list); + free(port); +} + +static int tegra_pcie_enable(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + debug("probing port %u, using %u lanes\n", port->index, + port->num_lanes); + + tegra_pcie_port_enable(port); + + if (tegra_pcie_port_check_link(port)) + continue; + + debug("link %u down, ignoring\n", port->index); + + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } + + return 0; +} + +static const struct tegra_pcie_soc tegra20_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, + .has_pex_clkreq_en = false, + .has_pex_bias_ctrl = false, + .has_cml_clk = false, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra30_pcie_soc = { + .num_ports = 3, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra124_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = true, +}; + +static int process_nodes(const void *fdt, int nodes[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + const struct tegra_pcie_soc *soc; + struct tegra_pcie *pcie; + enum fdt_compat_id id; + int err; + + if (!fdtdec_get_is_enabled(fdt, nodes[i])) + continue; + + id = fdtdec_lookup(fdt, nodes[i]); + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + soc = &tegra20_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + soc = &tegra30_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + soc = &tegra124_pcie_soc; + break; + + default: + error("unsupported compatible: %s", + fdtdec_get_compatible(id)); + continue; + } + + pcie = malloc(sizeof(*pcie)); + if (!pcie) { + error("failed to allocate controller"); + continue; + } + + memset(pcie, 0, sizeof(*pcie)); + pcie->soc = soc; + + INIT_LIST_HEAD(&pcie->ports); + + err = tegra_pcie_parse_dt(fdt, nodes[i], pcie); + if (err < 0) { + free(pcie); + continue; + } + + err = tegra_pcie_power_on(pcie); + if (err < 0) { + error("failed to power on"); + continue; + } + + err = tegra_pcie_enable_controller(pcie); + if (err < 0) { + error("failed to enable controller"); + continue; + } + + tegra_pcie_setup_translations(pcie); + + err = tegra_pcie_enable(pcie); + if (err < 0) { + error("failed to enable PCIe"); + continue; + } + + pcie->hose.first_busno = 0; + pcie->hose.current_busno = 0; + pcie->hose.last_busno = 0; + + pci_set_region(&pcie->hose.regions[0], NV_PA_SDRAM_BASE, + NV_PA_SDRAM_BASE, gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + pci_set_region(&pcie->hose.regions[1], pcie->io.start, + pcie->io.start, fdt_resource_size(&pcie->io), + PCI_REGION_IO); + + pci_set_region(&pcie->hose.regions[2], pcie->mem.start, + pcie->mem.start, fdt_resource_size(&pcie->mem), + PCI_REGION_MEM); + + pci_set_region(&pcie->hose.regions[3], pcie->prefetch.start, + pcie->prefetch.start, + fdt_resource_size(&pcie->prefetch), + PCI_REGION_MEM | PCI_REGION_PREFETCH); + + pcie->hose.region_count = 4; + + pci_set_ops(&pcie->hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + tegra_pcie_read_conf, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + tegra_pcie_write_conf); + + pci_register_hose(&pcie->hose); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Enumerating devices...\n"); + printf("---------------------------------------\n"); + printf(" Device ID Description\n"); + printf(" ------ -- -----------\n"); +#endif + + pcie->hose.last_busno = pci_hose_scan(&pcie->hose); + } + + return 0; +} + +void pci_init_board(void) +{ + const void *fdt = gd->fdt_blob; + int count, nodes[1]; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA124_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA30_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA20_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; +} + +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + if (PCI_BUS(dev) != 0 && PCI_DEV(dev) > 0) + return 1; + + return 0; +} diff --git a/include/fdtdec.h b/include/fdtdec.h index 364da6c67cd2..925133fa48d0 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -86,6 +86,9 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ + COMPAT_NVIDIA_TEGRA124_PCIE, /* Tegra 124 PCIe controller */ + COMPAT_NVIDIA_TEGRA30_PCIE, /* Tegra 30 PCIe controller */ + COMPAT_NVIDIA_TEGRA20_PCIE, /* Tegra 20 PCIe controller */ COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, /* Tegra124 XUSB pad controller */ COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index f4851592687f..9ec826205855 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -41,6 +41,9 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), + COMPAT(NVIDIA_TEGRA124_PCIE, "nvidia,tegra124-pcie"), + COMPAT(NVIDIA_TEGRA30_PCIE, "nvidia,tegra30-pcie"), + COMPAT(NVIDIA_TEGRA20_PCIE, "nvidia,tegra20-pcie"), COMPAT(NVIDIA_TEGRA124_XUSB_PADCTL, "nvidia,tegra124-xusb-padctl"), COMPAT(SMSC_LAN9215, "smsc,lan9215"), COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"),

From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra20 SoCs.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - add missing interrupt-map-mask and interrupt-map properties - include unit address in device tree node name
arch/arm/dts/tegra20.dtsi | 60 ++++++++++++ include/dt-bindings/clock/tegra20-car.h | 158 ++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 include/dt-bindings/clock/tegra20-car.h
diff --git a/arch/arm/dts/tegra20.dtsi b/arch/arm/dts/tegra20.dtsi index a524f6eed410..39976864fa48 100644 --- a/arch/arm/dts/tegra20.dtsi +++ b/arch/arm/dts/tegra20.dtsi @@ -1,3 +1,4 @@ +#include <dt-bindings/clock/tegra20-car.h> #include <dt-bindings/gpio/tegra-gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -301,6 +302,65 @@ reg = <0x7000f400 0x200>; };
+ pcie-controller@80003000 { + compatible = "nvidia,tegra20-pcie"; + device_type = "pci"; + reg = <0x80003000 0x00000800 /* PADS registers */ + 0x80003800 0x00000200 /* AFI registers */ + 0x90000000 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */ + GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x80000000 0x80000000 0 0x00001000 /* port 0 registers */ + 0x82000000 0 0x80001000 0x80001000 0 0x00001000 /* port 1 registers */ + 0x81000000 0 0 0x82000000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0xa0000000 0xa0000000 0 0x08000000 /* non-prefetchable memory */ + 0xc2000000 0 0xa8000000 0xa8000000 0 0x18000000>; /* prefetchable memory */ + + clocks = <&tegra_car TEGRA20_CLK_PEX>, + <&tegra_car TEGRA20_CLK_AFI>, + <&tegra_car TEGRA20_CLK_PCIE_XCLK>, + <&tegra_car TEGRA20_CLK_PLL_E>; + clock-names = "pex", "afi", "pcie_xclk", "pll_e"; + status = "disabled"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + }; + usb@c5000000 { compatible = "nvidia,tegra20-ehci", "usb-ehci"; reg = <0xc5000000 0x4000>; diff --git a/include/dt-bindings/clock/tegra20-car.h b/include/dt-bindings/clock/tegra20-car.h new file mode 100644 index 000000000000..a1ae9a8fdd6c --- /dev/null +++ b/include/dt-bindings/clock/tegra20-car.h @@ -0,0 +1,158 @@ +/* + * This header provides constants for binding nvidia,tegra20-car. + * + * The first 96 clocks are numbered to match the bits in the CAR's CLK_OUT_ENB + * registers. These IDs often match those in the CAR's RST_DEVICES registers, + * but not in all cases. Some bits in CLK_OUT_ENB affect multiple clocks. In + * this case, those clocks are assigned IDs above 95 in order to highlight + * this issue. Implementations that interpret these clock IDs as bit values + * within the CLK_OUT_ENB or RST_DEVICES registers should be careful to + * explicitly handle these special cases. + * + * The balance of the clocks controlled by the CAR are assigned IDs of 96 and + * above. + */ + +#ifndef _DT_BINDINGS_CLOCK_TEGRA20_CAR_H +#define _DT_BINDINGS_CLOCK_TEGRA20_CAR_H + +#define TEGRA20_CLK_CPU 0 +/* 1 */ +/* 2 */ +#define TEGRA20_CLK_AC97 3 +#define TEGRA20_CLK_RTC 4 +#define TEGRA20_CLK_TIMER 5 +#define TEGRA20_CLK_UARTA 6 +/* 7 (register bit affects uart2 and vfir) */ +#define TEGRA20_CLK_GPIO 8 +#define TEGRA20_CLK_SDMMC2 9 +/* 10 (register bit affects spdif_in and spdif_out) */ +#define TEGRA20_CLK_I2S1 11 +#define TEGRA20_CLK_I2C1 12 +#define TEGRA20_CLK_NDFLASH 13 +#define TEGRA20_CLK_SDMMC1 14 +#define TEGRA20_CLK_SDMMC4 15 +#define TEGRA20_CLK_TWC 16 +#define TEGRA20_CLK_PWM 17 +#define TEGRA20_CLK_I2S2 18 +#define TEGRA20_CLK_EPP 19 +/* 20 (register bit affects vi and vi_sensor) */ +#define TEGRA20_CLK_GR2D 21 +#define TEGRA20_CLK_USBD 22 +#define TEGRA20_CLK_ISP 23 +#define TEGRA20_CLK_GR3D 24 +#define TEGRA20_CLK_IDE 25 +#define TEGRA20_CLK_DISP2 26 +#define TEGRA20_CLK_DISP1 27 +#define TEGRA20_CLK_HOST1X 28 +#define TEGRA20_CLK_VCP 29 +/* 30 */ +#define TEGRA20_CLK_CACHE2 31 + +#define TEGRA20_CLK_MEM 32 +#define TEGRA20_CLK_AHBDMA 33 +#define TEGRA20_CLK_APBDMA 34 +/* 35 */ +#define TEGRA20_CLK_KBC 36 +#define TEGRA20_CLK_STAT_MON 37 +#define TEGRA20_CLK_PMC 38 +#define TEGRA20_CLK_FUSE 39 +#define TEGRA20_CLK_KFUSE 40 +#define TEGRA20_CLK_SBC1 41 +#define TEGRA20_CLK_NOR 42 +#define TEGRA20_CLK_SPI 43 +#define TEGRA20_CLK_SBC2 44 +#define TEGRA20_CLK_XIO 45 +#define TEGRA20_CLK_SBC3 46 +#define TEGRA20_CLK_DVC 47 +#define TEGRA20_CLK_DSI 48 +/* 49 (register bit affects tvo and cve) */ +#define TEGRA20_CLK_MIPI 50 +#define TEGRA20_CLK_HDMI 51 +#define TEGRA20_CLK_CSI 52 +#define TEGRA20_CLK_TVDAC 53 +#define TEGRA20_CLK_I2C2 54 +#define TEGRA20_CLK_UARTC 55 +/* 56 */ +#define TEGRA20_CLK_EMC 57 +#define TEGRA20_CLK_USB2 58 +#define TEGRA20_CLK_USB3 59 +#define TEGRA20_CLK_MPE 60 +#define TEGRA20_CLK_VDE 61 +#define TEGRA20_CLK_BSEA 62 +#define TEGRA20_CLK_BSEV 63 + +#define TEGRA20_CLK_SPEEDO 64 +#define TEGRA20_CLK_UARTD 65 +#define TEGRA20_CLK_UARTE 66 +#define TEGRA20_CLK_I2C3 67 +#define TEGRA20_CLK_SBC4 68 +#define TEGRA20_CLK_SDMMC3 69 +#define TEGRA20_CLK_PEX 70 +#define TEGRA20_CLK_OWR 71 +#define TEGRA20_CLK_AFI 72 +#define TEGRA20_CLK_CSITE 73 +#define TEGRA20_CLK_PCIE_XCLK 74 +#define TEGRA20_CLK_AVPUCQ 75 +#define TEGRA20_CLK_LA 76 +/* 77 */ +/* 78 */ +/* 79 */ +/* 80 */ +/* 81 */ +/* 82 */ +/* 83 */ +#define TEGRA20_CLK_IRAMA 84 +#define TEGRA20_CLK_IRAMB 85 +#define TEGRA20_CLK_IRAMC 86 +#define TEGRA20_CLK_IRAMD 87 +#define TEGRA20_CLK_CRAM2 88 +#define TEGRA20_CLK_AUDIO_2X 89 /* a/k/a audio_2x_sync_clk */ +#define TEGRA20_CLK_CLK_D 90 +/* 91 */ +#define TEGRA20_CLK_CSUS 92 +#define TEGRA20_CLK_CDEV2 93 +#define TEGRA20_CLK_CDEV1 94 +/* 95 */ + +#define TEGRA20_CLK_UARTB 96 +#define TEGRA20_CLK_VFIR 97 +#define TEGRA20_CLK_SPDIF_IN 98 +#define TEGRA20_CLK_SPDIF_OUT 99 +#define TEGRA20_CLK_VI 100 +#define TEGRA20_CLK_VI_SENSOR 101 +#define TEGRA20_CLK_TVO 102 +#define TEGRA20_CLK_CVE 103 +#define TEGRA20_CLK_OSC 104 +#define TEGRA20_CLK_CLK_32K 105 /* a/k/a clk_s */ +#define TEGRA20_CLK_CLK_M 106 +#define TEGRA20_CLK_SCLK 107 +#define TEGRA20_CLK_CCLK 108 +#define TEGRA20_CLK_HCLK 109 +#define TEGRA20_CLK_PCLK 110 +#define TEGRA20_CLK_BLINK 111 +#define TEGRA20_CLK_PLL_A 112 +#define TEGRA20_CLK_PLL_A_OUT0 113 +#define TEGRA20_CLK_PLL_C 114 +#define TEGRA20_CLK_PLL_C_OUT1 115 +#define TEGRA20_CLK_PLL_D 116 +#define TEGRA20_CLK_PLL_D_OUT0 117 +#define TEGRA20_CLK_PLL_E 118 +#define TEGRA20_CLK_PLL_M 119 +#define TEGRA20_CLK_PLL_M_OUT1 120 +#define TEGRA20_CLK_PLL_P 121 +#define TEGRA20_CLK_PLL_P_OUT1 122 +#define TEGRA20_CLK_PLL_P_OUT2 123 +#define TEGRA20_CLK_PLL_P_OUT3 124 +#define TEGRA20_CLK_PLL_P_OUT4 125 +#define TEGRA20_CLK_PLL_S 126 +#define TEGRA20_CLK_PLL_U 127 + +#define TEGRA20_CLK_PLL_X 128 +#define TEGRA20_CLK_COP 129 /* a/k/a avp */ +#define TEGRA20_CLK_AUDIO 130 /* a/k/a audio_sync_clk */ +#define TEGRA20_CLK_PLL_REF 131 +#define TEGRA20_CLK_TWD 132 +#define TEGRA20_CLK_CLK_MAX 133 + +#endif /* _DT_BINDINGS_CLOCK_TEGRA20_CAR_H */

From: Thierry Reding treding@nvidia.com
The TrimSlice has an ethernet NIC connected to the PCIe bus. Enable the PCIe controller and the network driver so that the device can boot over the network.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - add all regulators from Linux kernel DTS - use proper set of PCIe power supplies - use unit-address in node name
arch/arm/dts/tegra20-trimslice.dts | 69 ++++++++++++++++++++++++++++++++++++ board/compulab/trimslice/trimslice.c | 8 +++++ include/configs/trimslice.h | 10 ++++++ 3 files changed, 87 insertions(+)
diff --git a/arch/arm/dts/tegra20-trimslice.dts b/arch/arm/dts/tegra20-trimslice.dts index ee31476c1eb8..9d6ec12e6d00 100644 --- a/arch/arm/dts/tegra20-trimslice.dts +++ b/arch/arm/dts/tegra20-trimslice.dts @@ -42,6 +42,20 @@ status = "disabled"; };
+ pcie-controller@80003000 { + status = "okay"; + + avdd-pex-supply = <&pci_vdd_reg>; + vdd-pex-supply = <&pci_vdd_reg>; + avdd-pex-pll-supply = <&pci_vdd_reg>; + avdd-plle-supply = <&pci_vdd_reg>; + vddio-pex-clk-supply = <&pci_clk_reg>; + + pci@1,0 { + status = "okay"; + }; + }; + usb@c5000000 { nvidia,vbus-gpio = <&gpio 170 0>; /* PV2 */ }; @@ -61,4 +75,59 @@ wp-gpios = <&gpio 122 0>; /* gpio PP2 */ bus-width = <4>; }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_vdd_reg: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "avdd_hdmi"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + hdmi_pll_reg: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "avdd_hdmi_pll"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vbus_reg: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(V, 2) 0>; + regulator-always-on; + regulator-boot-on; + }; + + pci_clk_reg: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + regulator-name = "pci_clk"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + pci_vdd_reg: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "pci_vdd"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-always-on; + }; + }; + }; diff --git a/board/compulab/trimslice/trimslice.c b/board/compulab/trimslice/trimslice.c index 723293fef35a..c9da80d5eb1c 100644 --- a/board/compulab/trimslice/trimslice.c +++ b/board/compulab/trimslice/trimslice.c @@ -13,6 +13,7 @@ #include <asm/arch/pinmux.h> #include <asm/gpio.h> #include <i2c.h> +#include <netdev.h>
void pin_mux_usb(void) { @@ -40,3 +41,10 @@ void pin_mux_mmc(void) /* For CD GPIO PP1 */ pinmux_tristate_disable(PMUX_PINGRP_DAP3); } + +#ifdef CONFIG_PCI +int board_eth_init(bd_t *bis) +{ + return pci_eth_init(bis); +} +#endif diff --git a/include/configs/trimslice.h b/include/configs/trimslice.h index f81cfa2e354d..285dc7a9e660 100644 --- a/include/configs/trimslice.h +++ b/include/configs/trimslice.h @@ -68,6 +68,16 @@ #define CONFIG_USB_HOST_ETHER #define CONFIG_USB_ETHER_ASIX
+/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_TEGRA +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI +#define CONFIG_CMD_PCI_ENUM + +/* PCI networking support */ +#define CONFIG_RTL8169 + /* General networking support */ #define CONFIG_CMD_NET #define CONFIG_CMD_DHCP

From: Thierry Reding treding@nvidia.com
Add a device tree node for the GIC found on Tegra30. U-Boot doesn't use it directly but subsequent patches will add device tree nodes that reference it by phandle.
Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra30.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi index 7be3791fc9a6..8b8940df796a 100644 --- a/arch/arm/dts/tegra30.dtsi +++ b/arch/arm/dts/tegra30.dtsi @@ -5,6 +5,15 @@
/ { compatible = "nvidia,tegra30"; + interrupt-parent = <&intc>; + + intc: interrupt-controller@50041000 { + compatible = "arm,cortex-a9-gic"; + reg = <0x50041000 0x1000 + 0x50040100 0x0100>; + interrupt-controller; + #interrupt-cells = <3>; + };
tegra_car: clock { compatible = "nvidia,tegra30-car";

From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra30 SoCs.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - include unit address in device tree node name - add missing interrupt-map-mask and interrupt-map properties
arch/arm/dts/tegra30.dtsi | 75 +++++++++ include/dt-bindings/clock/tegra30-car.h | 265 ++++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 include/dt-bindings/clock/tegra30-car.h
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi index 8b8940df796a..521fdc9600e7 100644 --- a/arch/arm/dts/tegra30.dtsi +++ b/arch/arm/dts/tegra30.dtsi @@ -1,3 +1,4 @@ +#include <dt-bindings/clock/tegra30-car.h> #include <dt-bindings/gpio/tegra-gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -15,6 +16,80 @@ #interrupt-cells = <3>; };
+ pcie-controller@00003000 { + compatible = "nvidia,tegra30-pcie"; + device_type = "pci"; + reg = <0x00003000 0x00000800 /* PADS registers */ + 0x00003800 0x00000200 /* AFI registers */ + 0x10000000 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */ + GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x00000000 0x00000000 0 0x00001000 /* port 0 configuration space */ + 0x82000000 0 0x00001000 0x00001000 0 0x00001000 /* port 1 configuration space */ + 0x82000000 0 0x00004000 0x00004000 0 0x00001000 /* port 2 configuration space */ + 0x81000000 0 0 0x02000000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x20000000 0x20000000 0 0x10000000 /* non-prefetchable memory */ + 0xc2000000 0 0x30000000 0x30000000 0 0x10000000>; /* prefetchable memory */ + + clocks = <&tegra_car TEGRA30_CLK_PCIE>, + <&tegra_car TEGRA30_CLK_AFI>, + <&tegra_car TEGRA30_CLK_PCIEX>, + <&tegra_car TEGRA30_CLK_PLL_E>, + <&tegra_car TEGRA30_CLK_CML0>; + clock-names = "pex", "afi", "pcie_xclk", "pll_e", "cml"; + status = "disabled"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x00000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x00001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@3,0 { + device_type = "pci"; + assigned-addresses = <0x82001800 0 0x00004000 0 0x1000>; + reg = <0x001800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + }; + tegra_car: clock { compatible = "nvidia,tegra30-car"; reg = <0x60006000 0x1000>; diff --git a/include/dt-bindings/clock/tegra30-car.h b/include/dt-bindings/clock/tegra30-car.h new file mode 100644 index 000000000000..e40fae8f9a8d --- /dev/null +++ b/include/dt-bindings/clock/tegra30-car.h @@ -0,0 +1,265 @@ +/* + * This header provides constants for binding nvidia,tegra30-car. + * + * The first 130 clocks are numbered to match the bits in the CAR's CLK_OUT_ENB + * registers. These IDs often match those in the CAR's RST_DEVICES registers, + * but not in all cases. Some bits in CLK_OUT_ENB affect multiple clocks. In + * this case, those clocks are assigned IDs above 160 in order to highlight + * this issue. Implementations that interpret these clock IDs as bit values + * within the CLK_OUT_ENB or RST_DEVICES registers should be careful to + * explicitly handle these special cases. + * + * The balance of the clocks controlled by the CAR are assigned IDs of 160 and + * above. + */ + +#ifndef _DT_BINDINGS_CLOCK_TEGRA30_CAR_H +#define _DT_BINDINGS_CLOCK_TEGRA30_CAR_H + +#define TEGRA30_CLK_CPU 0 +/* 1 */ +/* 2 */ +/* 3 */ +#define TEGRA30_CLK_RTC 4 +#define TEGRA30_CLK_TIMER 5 +#define TEGRA30_CLK_UARTA 6 +/* 7 (register bit affects uartb and vfir) */ +#define TEGRA30_CLK_GPIO 8 +#define TEGRA30_CLK_SDMMC2 9 +/* 10 (register bit affects spdif_in and spdif_out) */ +#define TEGRA30_CLK_I2S1 11 +#define TEGRA30_CLK_I2C1 12 +#define TEGRA30_CLK_NDFLASH 13 +#define TEGRA30_CLK_SDMMC1 14 +#define TEGRA30_CLK_SDMMC4 15 +/* 16 */ +#define TEGRA30_CLK_PWM 17 +#define TEGRA30_CLK_I2S2 18 +#define TEGRA30_CLK_EPP 19 +/* 20 (register bit affects vi and vi_sensor) */ +#define TEGRA30_CLK_GR2D 21 +#define TEGRA30_CLK_USBD 22 +#define TEGRA30_CLK_ISP 23 +#define TEGRA30_CLK_GR3D 24 +/* 25 */ +#define TEGRA30_CLK_DISP2 26 +#define TEGRA30_CLK_DISP1 27 +#define TEGRA30_CLK_HOST1X 28 +#define TEGRA30_CLK_VCP 29 +#define TEGRA30_CLK_I2S0 30 +#define TEGRA30_CLK_COP_CACHE 31 + +#define TEGRA30_CLK_MC 32 +#define TEGRA30_CLK_AHBDMA 33 +#define TEGRA30_CLK_APBDMA 34 +/* 35 */ +#define TEGRA30_CLK_KBC 36 +#define TEGRA30_CLK_STATMON 37 +#define TEGRA30_CLK_PMC 38 +/* 39 (register bit affects fuse and fuse_burn) */ +#define TEGRA30_CLK_KFUSE 40 +#define TEGRA30_CLK_SBC1 41 +#define TEGRA30_CLK_NOR 42 +/* 43 */ +#define TEGRA30_CLK_SBC2 44 +/* 45 */ +#define TEGRA30_CLK_SBC3 46 +#define TEGRA30_CLK_I2C5 47 +#define TEGRA30_CLK_DSIA 48 +/* 49 (register bit affects cve and tvo) */ +#define TEGRA30_CLK_MIPI 50 +#define TEGRA30_CLK_HDMI 51 +#define TEGRA30_CLK_CSI 52 +#define TEGRA30_CLK_TVDAC 53 +#define TEGRA30_CLK_I2C2 54 +#define TEGRA30_CLK_UARTC 55 +/* 56 */ +#define TEGRA30_CLK_EMC 57 +#define TEGRA30_CLK_USB2 58 +#define TEGRA30_CLK_USB3 59 +#define TEGRA30_CLK_MPE 60 +#define TEGRA30_CLK_VDE 61 +#define TEGRA30_CLK_BSEA 62 +#define TEGRA30_CLK_BSEV 63 + +#define TEGRA30_CLK_SPEEDO 64 +#define TEGRA30_CLK_UARTD 65 +#define TEGRA30_CLK_UARTE 66 +#define TEGRA30_CLK_I2C3 67 +#define TEGRA30_CLK_SBC4 68 +#define TEGRA30_CLK_SDMMC3 69 +#define TEGRA30_CLK_PCIE 70 +#define TEGRA30_CLK_OWR 71 +#define TEGRA30_CLK_AFI 72 +#define TEGRA30_CLK_CSITE 73 +#define TEGRA30_CLK_PCIEX 74 +#define TEGRA30_CLK_AVPUCQ 75 +#define TEGRA30_CLK_LA 76 +/* 77 */ +/* 78 */ +#define TEGRA30_CLK_DTV 79 +#define TEGRA30_CLK_NDSPEED 80 +#define TEGRA30_CLK_I2CSLOW 81 +#define TEGRA30_CLK_DSIB 82 +/* 83 */ +#define TEGRA30_CLK_IRAMA 84 +#define TEGRA30_CLK_IRAMB 85 +#define TEGRA30_CLK_IRAMC 86 +#define TEGRA30_CLK_IRAMD 87 +#define TEGRA30_CLK_CRAM2 88 +/* 89 */ +#define TEGRA30_CLK_AUDIO_2X 90 /* a/k/a audio_2x_sync_clk */ +/* 91 */ +#define TEGRA30_CLK_CSUS 92 +#define TEGRA30_CLK_CDEV2 93 +#define TEGRA30_CLK_CDEV1 94 +/* 95 */ + +#define TEGRA30_CLK_CPU_G 96 +#define TEGRA30_CLK_CPU_LP 97 +#define TEGRA30_CLK_GR3D2 98 +#define TEGRA30_CLK_MSELECT 99 +#define TEGRA30_CLK_TSENSOR 100 +#define TEGRA30_CLK_I2S3 101 +#define TEGRA30_CLK_I2S4 102 +#define TEGRA30_CLK_I2C4 103 +#define TEGRA30_CLK_SBC5 104 +#define TEGRA30_CLK_SBC6 105 +#define TEGRA30_CLK_D_AUDIO 106 +#define TEGRA30_CLK_APBIF 107 +#define TEGRA30_CLK_DAM0 108 +#define TEGRA30_CLK_DAM1 109 +#define TEGRA30_CLK_DAM2 110 +#define TEGRA30_CLK_HDA2CODEC_2X 111 +#define TEGRA30_CLK_ATOMICS 112 +#define TEGRA30_CLK_AUDIO0_2X 113 +#define TEGRA30_CLK_AUDIO1_2X 114 +#define TEGRA30_CLK_AUDIO2_2X 115 +#define TEGRA30_CLK_AUDIO3_2X 116 +#define TEGRA30_CLK_AUDIO4_2X 117 +#define TEGRA30_CLK_SPDIF_2X 118 +#define TEGRA30_CLK_ACTMON 119 +#define TEGRA30_CLK_EXTERN1 120 +#define TEGRA30_CLK_EXTERN2 121 +#define TEGRA30_CLK_EXTERN3 122 +#define TEGRA30_CLK_SATA_OOB 123 +#define TEGRA30_CLK_SATA 124 +#define TEGRA30_CLK_HDA 125 +/* 126 */ +#define TEGRA30_CLK_SE 127 + +#define TEGRA30_CLK_HDA2HDMI 128 +#define TEGRA30_CLK_SATA_COLD 129 +/* 130 */ +/* 131 */ +/* 132 */ +/* 133 */ +/* 134 */ +/* 135 */ +/* 136 */ +/* 137 */ +/* 138 */ +/* 139 */ +/* 140 */ +/* 141 */ +/* 142 */ +/* 143 */ +/* 144 */ +/* 145 */ +/* 146 */ +/* 147 */ +/* 148 */ +/* 149 */ +/* 150 */ +/* 151 */ +/* 152 */ +/* 153 */ +/* 154 */ +/* 155 */ +/* 156 */ +/* 157 */ +/* 158 */ +/* 159 */ + +#define TEGRA30_CLK_UARTB 160 +#define TEGRA30_CLK_VFIR 161 +#define TEGRA30_CLK_SPDIF_IN 162 +#define TEGRA30_CLK_SPDIF_OUT 163 +#define TEGRA30_CLK_VI 164 +#define TEGRA30_CLK_VI_SENSOR 165 +#define TEGRA30_CLK_FUSE 166 +#define TEGRA30_CLK_FUSE_BURN 167 +#define TEGRA30_CLK_CVE 168 +#define TEGRA30_CLK_TVO 169 +#define TEGRA30_CLK_CLK_32K 170 +#define TEGRA30_CLK_CLK_M 171 +#define TEGRA30_CLK_CLK_M_DIV2 172 +#define TEGRA30_CLK_CLK_M_DIV4 173 +#define TEGRA30_CLK_PLL_REF 174 +#define TEGRA30_CLK_PLL_C 175 +#define TEGRA30_CLK_PLL_C_OUT1 176 +#define TEGRA30_CLK_PLL_M 177 +#define TEGRA30_CLK_PLL_M_OUT1 178 +#define TEGRA30_CLK_PLL_P 179 +#define TEGRA30_CLK_PLL_P_OUT1 180 +#define TEGRA30_CLK_PLL_P_OUT2 181 +#define TEGRA30_CLK_PLL_P_OUT3 182 +#define TEGRA30_CLK_PLL_P_OUT4 183 +#define TEGRA30_CLK_PLL_A 184 +#define TEGRA30_CLK_PLL_A_OUT0 185 +#define TEGRA30_CLK_PLL_D 186 +#define TEGRA30_CLK_PLL_D_OUT0 187 +#define TEGRA30_CLK_PLL_D2 188 +#define TEGRA30_CLK_PLL_D2_OUT0 189 +#define TEGRA30_CLK_PLL_U 190 +#define TEGRA30_CLK_PLL_X 191 + +#define TEGRA30_CLK_PLL_X_OUT0 192 +#define TEGRA30_CLK_PLL_E 193 +#define TEGRA30_CLK_SPDIF_IN_SYNC 194 +#define TEGRA30_CLK_I2S0_SYNC 195 +#define TEGRA30_CLK_I2S1_SYNC 196 +#define TEGRA30_CLK_I2S2_SYNC 197 +#define TEGRA30_CLK_I2S3_SYNC 198 +#define TEGRA30_CLK_I2S4_SYNC 199 +#define TEGRA30_CLK_VIMCLK_SYNC 200 +#define TEGRA30_CLK_AUDIO0 201 +#define TEGRA30_CLK_AUDIO1 202 +#define TEGRA30_CLK_AUDIO2 203 +#define TEGRA30_CLK_AUDIO3 204 +#define TEGRA30_CLK_AUDIO4 205 +#define TEGRA30_CLK_SPDIF 206 +#define TEGRA30_CLK_CLK_OUT_1 207 /* (extern1) */ +#define TEGRA30_CLK_CLK_OUT_2 208 /* (extern2) */ +#define TEGRA30_CLK_CLK_OUT_3 209 /* (extern3) */ +#define TEGRA30_CLK_SCLK 210 +#define TEGRA30_CLK_BLINK 211 +#define TEGRA30_CLK_CCLK_G 212 +#define TEGRA30_CLK_CCLK_LP 213 +#define TEGRA30_CLK_TWD 214 +#define TEGRA30_CLK_CML0 215 +#define TEGRA30_CLK_CML1 216 +#define TEGRA30_CLK_HCLK 217 +#define TEGRA30_CLK_PCLK 218 +/* 219 */ +/* 220 */ +/* 221 */ +/* 222 */ +/* 223 */ + +/* 288 */ +/* 289 */ +/* 290 */ +/* 291 */ +/* 292 */ +/* 293 */ +/* 294 */ +/* 295 */ +/* 296 */ +/* 297 */ +/* 298 */ +/* 299 */ +#define TEGRA30_CLK_CLK_OUT_1_MUX 300 +#define TEGRA30_CLK_CLK_MAX 301 + +#endif /* _DT_BINDINGS_CLOCK_TEGRA30_CAR_H */

From: Thierry Reding treding@nvidia.com
The PCIe bus on Cardhu is routed to the dock connector. An ethernet NIC is available on the dock over the PCIe bus. Enable the PCIe controller and the network device driver so that the device can boot over the network.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - write to instead of read from the LDO2_REG register to enable LDO2 - use proper set of PCIe power supplies - use unit address in node name
arch/arm/dts/tegra30-cardhu.dts | 362 ++++++++++++++++++++++++++++++++++++++++ board/nvidia/cardhu/cardhu.c | 56 +++++++ include/configs/cardhu.h | 10 ++ 3 files changed, 428 insertions(+)
diff --git a/arch/arm/dts/tegra30-cardhu.dts b/arch/arm/dts/tegra30-cardhu.dts index ea2cf76ff3a8..54e08f59da12 100644 --- a/arch/arm/dts/tegra30-cardhu.dts +++ b/arch/arm/dts/tegra30-cardhu.dts @@ -22,6 +22,31 @@ reg = <0x80000000 0x40000000>; };
+ pcie-controller@00003000 { + status = "okay"; + + /* AVDD_PEXA and VDD_PEXA inputs are grounded on Cardhu. */ + avdd-pexb-supply = <&ldo1_reg>; + vdd-pexb-supply = <&ldo1_reg>; + avdd-pex-pll-supply = <&ldo1_reg>; + hvdd-pex-supply = <&pex_hvdd_3v3_reg>; + vddio-pex-ctl-supply = <&sys_3v3_reg>; + avdd-plle-supply = <&ldo2_reg>; + + pci@1,0 { + nvidia,num-lanes = <4>; + }; + + pci@2,0 { + nvidia,num-lanes = <1>; + }; + + pci@3,0 { + status = "okay"; + nvidia,num-lanes = <1>; + }; + }; + i2c@7000c000 { status = "okay"; clock-frequency = <100000>; @@ -45,6 +70,107 @@ i2c@7000d000 { status = "okay"; clock-frequency = <100000>; + + pmic: tps65911@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <2>; + interrupt-controller; + + ti,system-power-controller; + + #gpio-cells = <2>; + gpio-controller; + + vcc1-supply = <&vdd_ac_bat_reg>; + vcc2-supply = <&vdd_ac_bat_reg>; + vcc3-supply = <&vio_reg>; + vcc4-supply = <&vdd_5v0_reg>; + vcc5-supply = <&vdd_ac_bat_reg>; + vcc6-supply = <&vdd2_reg>; + vcc7-supply = <&vdd_ac_bat_reg>; + vccio-supply = <&vdd_ac_bat_reg>; + + regulators { + vdd1_reg: vdd1 { + regulator-name = "vddio_ddr_1v2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + vdd2_reg: vdd2 { + regulator-name = "vdd_1v5_gen"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + vddctrl_reg: vddctrl { + regulator-name = "vdd_cpu,vdd_sys"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + vio_reg: vio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo1_reg: ldo1 { + regulator-name = "vdd_pexa,vdd_pexb"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + ldo2_reg: ldo2 { + regulator-name = "vdd_sata,avdd_plle"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + /* LDO3 is not connected to anything */ + + ldo4_reg: ldo4 { + regulator-name = "vdd_rtc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + ldo5_reg: ldo5 { + regulator-name = "vddio_sdmmc,avdd_vdac"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo6_reg: ldo6 { + regulator-name = "avdd_dsi_csi,pwrdet_mipi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo7_reg: ldo7 { + regulator-name = "vdd_pllm,x,u,a_p_c_s"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + ldo8_reg: ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + }; + }; };
spi@7000da00 { @@ -69,4 +195,240 @@ nvidia,vbus-gpio = <&gpio 236 0>; /* PDD4 */ status = "okay"; }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_ac_bat_reg: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "vdd_ac_bat"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + cam_1v8_reg: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "cam_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(BB, 4) GPIO_ACTIVE_HIGH>; + vin-supply = <&vio_reg>; + }; + + cp_5v_reg: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "cp_5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + enable-active-high; + gpio = <&pmic 0 GPIO_ACTIVE_HIGH>; + }; + + emmc_3v3_reg: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + regulator-name = "emmc_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(D, 1) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + modem_3v3_reg: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "modem_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(D, 6) GPIO_ACTIVE_HIGH>; + }; + + pex_hvdd_3v3_reg: regulator@5 { + compatible = "regulator-fixed"; + reg = <5>; + regulator-name = "pex_hvdd_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(L, 7) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_cam1_ldo_reg: regulator@6 { + compatible = "regulator-fixed"; + reg = <6>; + regulator-name = "vdd_cam1_ldo"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(R, 6) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_cam2_ldo_reg: regulator@7 { + compatible = "regulator-fixed"; + reg = <7>; + regulator-name = "vdd_cam2_ldo"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_cam3_ldo_reg: regulator@8 { + compatible = "regulator-fixed"; + reg = <8>; + regulator-name = "vdd_cam3_ldo"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(S, 0) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_com_reg: regulator@9 { + compatible = "regulator-fixed"; + reg = <9>; + regulator-name = "vdd_com"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(D, 0) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_fuse_3v3_reg: regulator@10 { + compatible = "regulator-fixed"; + reg = <10>; + regulator-name = "vdd_fuse_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(L, 6) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_pnl1_reg: regulator@11 { + compatible = "regulator-fixed"; + reg = <11>; + regulator-name = "vdd_pnl1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(L, 4) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_vid_reg: regulator@12 { + compatible = "regulator-fixed"; + reg = <12>; + regulator-name = "vddio_vid"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(T, 0) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v0_reg>; + }; + + ddr_reg: regulator@100 { + compatible = "regulator-fixed"; + regulator-name = "ddr"; + reg = <100>; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&pmic 7 GPIO_ACTIVE_HIGH>; + }; + + sys_3v3_reg: regulator@101 { + compatible = "regulator-fixed"; + reg = <101>; + regulator-name = "sys_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&pmic 6 GPIO_ACTIVE_HIGH>; + }; + + usb1_vbus_reg: regulator@102 { + compatible = "regulator-fixed"; + reg = <102>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 6) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v0_reg>; + }; + + usb3_vbus_reg: regulator@103 { + compatible = "regulator-fixed"; + reg = <103>; + regulator-name = "usb3_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 4) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v0_reg>; + }; + + vdd_5v0_reg: regulator@104 { + compatible = "regulator-fixed"; + reg = <104>; + regulator-name = "5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&pmic 8 GPIO_ACTIVE_HIGH>; + }; + + vdd_bl_reg: regulator@105 { + compatible = "regulator-fixed"; + reg = <105>; + regulator-name = "vdd_bl"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 2) GPIO_ACTIVE_HIGH>; + }; + + vdd_bl2_reg: regulator@106 { + compatible = "regulator-fixed"; + reg = <106>; + regulator-name = "vdd_bl2"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 0) GPIO_ACTIVE_HIGH>; + }; + }; }; diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index cc0e5e130fda..7893bc3db81c 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -8,8 +8,11 @@ #include <common.h> #include <asm/arch/pinmux.h> #include <asm/arch/gp_padctrl.h> +#include <asm/arch/gpio.h> +#include <asm/gpio.h> #include "pinmux-config-cardhu.h" #include <i2c.h> +#include <netdev.h>
#define PMU_I2C_ADDRESS 0x2D #define MAX_I2C_RETRY 3 @@ -76,3 +79,56 @@ void pin_mux_mmc(void) board_sdmmc_voltage_init(); } #endif /* MMC */ + +#ifdef CONFIG_PCI_TEGRA +int tegra_pcie_board_init(void) +{ + unsigned int old_bus; + u8 addr, data[1]; + int err; + + old_bus = i2c_get_bus_num(); + + err = i2c_set_bus_num(0); + if (err) { + debug("failed to set I2C bus\n"); + return err; + } + + /* TPS659110: LDO1_REG = 1.05V, ACTIVE */ + data[0] = 0x15; + addr = 0x30; + + err = i2c_write(PMU_I2C_ADDRESS, addr, 1, data, 1); + if (err) { + debug("failed to set VDD supply\n"); + return err; + } + + /* GPIO: PEX = 3.3V */ + err = gpio_request(GPIO_PL7, "PEX"); + if (err < 0) + return err; + + gpio_direction_output(GPIO_PL7, 1); + + /* TPS659110: LDO2_REG = 1.05V, ACTIVE */ + data[0] = 0x15; + addr = 0x31; + + err = i2c_write(PMU_I2C_ADDRESS, addr, 1, data, 1); + if (err) { + debug("failed to set AVDD supply\n"); + return err; + } + + i2c_set_bus_num(old_bus); + + return 0; +} + +int board_eth_init(bd_t *bis) +{ + return pci_eth_init(bis); +} +#endif /* PCI */ diff --git a/include/configs/cardhu.h b/include/configs/cardhu.h index 59f429cf5784..f5f4a860f6a9 100644 --- a/include/configs/cardhu.h +++ b/include/configs/cardhu.h @@ -88,6 +88,16 @@ #define CONFIG_USB_HOST_ETHER #define CONFIG_USB_ETHER_ASIX
+/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_TEGRA +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI +#define CONFIG_CMD_PCI_ENUM + +/* PCI networking support */ +#define CONFIG_RTL8169 + /* General networking support */ #define CONFIG_CMD_NET #define CONFIG_CMD_DHCP

From: Thierry Reding treding@nvidia.com
The Beaver has an ethernet NIC connected to the PCIe bus. Enable the PCIe controller and the network device driver so that the device can boot over the network.
In addition the board has a mini-PCIe expansion slot.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - move after Cardhu PCIe patch and move most code into that patch - use proper set of PCIe power supplies - use unit address in node name
arch/arm/dts/tegra30-beaver.dts | 245 ++++++++++++++++++++++++++++++++++++++++ include/configs/beaver.h | 10 ++ 2 files changed, 255 insertions(+)
diff --git a/arch/arm/dts/tegra30-beaver.dts b/arch/arm/dts/tegra30-beaver.dts index 85e62e9db32e..4a4c07851202 100644 --- a/arch/arm/dts/tegra30-beaver.dts +++ b/arch/arm/dts/tegra30-beaver.dts @@ -23,6 +23,33 @@ reg = <0x80000000 0x7ff00000>; };
+ pcie-controller@00003000 { + status = "okay"; + + avdd-pexa-supply = <&ldo1_reg>; + vdd-pexa-supply = <&ldo1_reg>; + avdd-pexb-supply = <&ldo1_reg>; + vdd-pexb-supply = <&ldo1_reg>; + avdd-pex-pll-supply = <&ldo1_reg>; + avdd-plle-supply = <&ldo1_reg>; + vddio-pex-ctl-supply = <&sys_3v3_reg>; + hvdd-pex-supply = <&sys_3v3_pexs_reg>; + + pci@1,0 { + status = "okay"; + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + nvidia,num-lanes = <2>; + }; + + pci@3,0 { + status = "okay"; + nvidia,num-lanes = <2>; + }; + }; + i2c@7000c000 { status = "okay"; clock-frequency = <100000>; @@ -46,6 +73,110 @@ i2c@7000d000 { status = "okay"; clock-frequency = <100000>; + + pmic: tps65911@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <2>; + interrupt-controller; + + ti,system-power-controller; + + #gpio-cells = <2>; + gpio-controller; + + vcc1-supply = <&vdd_5v_in_reg>; + vcc2-supply = <&vdd_5v_in_reg>; + vcc3-supply = <&vio_reg>; + vcc4-supply = <&vdd_5v_in_reg>; + vcc5-supply = <&vdd_5v_in_reg>; + vcc6-supply = <&vdd2_reg>; + vcc7-supply = <&vdd_5v_in_reg>; + vccio-supply = <&vdd_5v_in_reg>; + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + vdd1_reg: vdd1 { + regulator-name = "vddio_ddr_1v2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + vdd2_reg: vdd2 { + regulator-name = "vdd_1v5_gen"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + vddctrl_reg: vddctrl { + regulator-name = "vdd_cpu,vdd_sys"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + vio_reg: vio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo1_reg: ldo1 { + regulator-name = "vdd_pexa,vdd_pexb"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + ldo2_reg: ldo2 { + regulator-name = "vdd_sata,avdd_plle"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + /* LDO3 is not connected to anything */ + + ldo4_reg: ldo4 { + regulator-name = "vdd_rtc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + ldo5_reg: ldo5 { + regulator-name = "vddio_sdmmc,avdd_vdac"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo6_reg: ldo6 { + regulator-name = "avdd_dsi_csi,pwrdet_mipi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo7_reg: ldo7 { + regulator-name = "vdd_pllm,x,u,a_p_c_s"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + ldo8_reg: ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + }; + }; };
spi@7000da00 { @@ -81,4 +212,118 @@ nvidia,vbus-gpio = <&gpio 236 0>; /* PDD4 */ status = "okay"; }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_5v_in_reg: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "vdd_5v_in"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + chargepump_5v_reg: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "chargepump_5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + enable-active-high; + gpio = <&pmic 0 GPIO_ACTIVE_HIGH>; + }; + + ddr_reg: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&pmic 7 GPIO_ACTIVE_HIGH>; + vin-supply = <&vdd_5v_in_reg>; + }; + + vdd_5v_sata_reg: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + regulator-name = "vdd_5v_sata"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(D, 6) GPIO_ACTIVE_HIGH>; + vin-supply = <&vdd_5v_in_reg>; + }; + + usb1_vbus_reg: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 6) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v_in_reg>; + }; + + usb3_vbus_reg: regulator@5 { + compatible = "regulator-fixed"; + reg = <5>; + regulator-name = "usb3_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(DD, 4) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v_in_reg>; + }; + + sys_3v3_reg: regulator@6 { + compatible = "regulator-fixed"; + reg = <6>; + regulator-name = "sys_3v3,vdd_3v3_alw"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&pmic 6 GPIO_ACTIVE_HIGH>; + vin-supply = <&vdd_5v_in_reg>; + }; + + sys_3v3_pexs_reg: regulator@7 { + compatible = "regulator-fixed"; + reg = <7>; + regulator-name = "sys_3v3_pexs"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(L, 7) GPIO_ACTIVE_HIGH>; + vin-supply = <&sys_3v3_reg>; + }; + + vdd_5v0_hdmi: regulator@8 { + compatible = "regulator-fixed"; + reg = <8>; + regulator-name = "+VDD_5V_HDMI"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&sys_3v3_reg>; + }; + }; }; diff --git a/include/configs/beaver.h b/include/configs/beaver.h index ae831127985f..bc96f6438d3c 100644 --- a/include/configs/beaver.h +++ b/include/configs/beaver.h @@ -84,6 +84,16 @@ #define CONFIG_USB_HOST_ETHER #define CONFIG_USB_ETHER_ASIX
+/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_TEGRA +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI +#define CONFIG_CMD_PCI_ENUM + +/* PCI networking support */ +#define CONFIG_RTL8169 + /* General networking support */ #define CONFIG_CMD_NET #define CONFIG_CMD_DHCP

From: Thierry Reding treding@nvidia.com
Add a device tree node for the GIC v2 found on the Cortex-A15 CPU complex of Tegra124. U-Boot doesn't use this but subsequent patches will add device tree nodes that reference it by phandle.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra124.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/arm/dts/tegra124.dtsi b/arch/arm/dts/tegra124.dtsi index d48f1a34ec3c..510fa332ef98 100644 --- a/arch/arm/dts/tegra124.dtsi +++ b/arch/arm/dts/tegra124.dtsi @@ -6,6 +6,19 @@
/ { compatible = "nvidia,tegra124"; + interrupt-parent = <&gic>; + + gic: interrupt-controller@50041000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x50041000 0x1000>, + <0x50042000 0x2000>, + <0x50044000 0x2000>, + <0x50046000 0x2000>; + interrupts = <GIC_PPI 9 + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>; + };
tegra_car: clock@60006000 { compatible = "nvidia,tegra124-car";

From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra124 SoCs.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra124.dtsi | 67 ++++++ include/dt-bindings/clock/tegra124-car.h | 341 +++++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+) create mode 100644 include/dt-bindings/clock/tegra124-car.h
diff --git a/arch/arm/dts/tegra124.dtsi b/arch/arm/dts/tegra124.dtsi index 510fa332ef98..d6dee96b12d6 100644 --- a/arch/arm/dts/tegra124.dtsi +++ b/arch/arm/dts/tegra124.dtsi @@ -1,3 +1,4 @@ +#include <dt-bindings/clock/tegra124-car.h> #include <dt-bindings/gpio/tegra-gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> @@ -8,6 +9,72 @@ compatible = "nvidia,tegra124"; interrupt-parent = <&gic>;
+ pcie-controller@01003000 { + compatible = "nvidia,tegra124-pcie"; + device_type = "pci"; + reg = <0x01003000 0x00000800 /* PADS registers */ + 0x01003800 0x00000800 /* AFI registers */ + 0x02000000 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */ + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x01000000 0x01000000 0 0x00001000 /* port 0 configuration space */ + 0x82000000 0 0x01001000 0x01001000 0 0x00001000 /* port 1 configuration space */ + 0x81000000 0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */ + 0x82000000 0 0x13000000 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */ + 0xc2000000 0 0x20000000 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */ + + clocks = <&tegra_car TEGRA124_CLK_PCIE>, + <&tegra_car TEGRA124_CLK_AFI>, + <&tegra_car TEGRA124_CLK_PLL_E>, + <&tegra_car TEGRA124_CLK_CML0>; + clock-names = "pex", "afi", "pll_e", "cml"; + resets = <&tegra_car 70>, + <&tegra_car 72>, + <&tegra_car 74>; + reset-names = "pex", "afi", "pcie_x"; + status = "disabled"; + + phys = <&padctl TEGRA_XUSB_PADCTL_PCIE>; + phy-names = "pcie"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <1>; + }; + }; + gic: interrupt-controller@50041000 { compatible = "arm,cortex-a15-gic"; #interrupt-cells = <3>; diff --git a/include/dt-bindings/clock/tegra124-car.h b/include/dt-bindings/clock/tegra124-car.h new file mode 100644 index 000000000000..433528ab5161 --- /dev/null +++ b/include/dt-bindings/clock/tegra124-car.h @@ -0,0 +1,341 @@ +/* + * This header provides constants for binding nvidia,tegra124-car. + * + * The first 192 clocks are numbered to match the bits in the CAR's CLK_OUT_ENB + * registers. These IDs often match those in the CAR's RST_DEVICES registers, + * but not in all cases. Some bits in CLK_OUT_ENB affect multiple clocks. In + * this case, those clocks are assigned IDs above 185 in order to highlight + * this issue. Implementations that interpret these clock IDs as bit values + * within the CLK_OUT_ENB or RST_DEVICES registers should be careful to + * explicitly handle these special cases. + * + * The balance of the clocks controlled by the CAR are assigned IDs of 185 and + * above. + */ + +#ifndef _DT_BINDINGS_CLOCK_TEGRA124_CAR_H +#define _DT_BINDINGS_CLOCK_TEGRA124_CAR_H + +/* 0 */ +/* 1 */ +/* 2 */ +#define TEGRA124_CLK_ISPB 3 +#define TEGRA124_CLK_RTC 4 +#define TEGRA124_CLK_TIMER 5 +#define TEGRA124_CLK_UARTA 6 +/* 7 (register bit affects uartb and vfir) */ +/* 8 */ +#define TEGRA124_CLK_SDMMC2 9 +/* 10 (register bit affects spdif_in and spdif_out) */ +#define TEGRA124_CLK_I2S1 11 +#define TEGRA124_CLK_I2C1 12 +/* 13 */ +#define TEGRA124_CLK_SDMMC1 14 +#define TEGRA124_CLK_SDMMC4 15 +/* 16 */ +#define TEGRA124_CLK_PWM 17 +#define TEGRA124_CLK_I2S2 18 +/* 20 (register bit affects vi and vi_sensor) */ +/* 21 */ +#define TEGRA124_CLK_USBD 22 +#define TEGRA124_CLK_ISP 23 +/* 26 */ +/* 25 */ +#define TEGRA124_CLK_DISP2 26 +#define TEGRA124_CLK_DISP1 27 +#define TEGRA124_CLK_HOST1X 28 +#define TEGRA124_CLK_VCP 29 +#define TEGRA124_CLK_I2S0 30 +/* 31 */ + +/* 32 */ +/* 33 */ +#define TEGRA124_CLK_APBDMA 34 +/* 35 */ +#define TEGRA124_CLK_KBC 36 +/* 37 */ +/* 38 */ +/* 39 (register bit affects fuse and fuse_burn) */ +#define TEGRA124_CLK_KFUSE 40 +#define TEGRA124_CLK_SBC1 41 +#define TEGRA124_CLK_NOR 42 +/* 43 */ +#define TEGRA124_CLK_SBC2 44 +/* 45 */ +#define TEGRA124_CLK_SBC3 46 +#define TEGRA124_CLK_I2C5 47 +#define TEGRA124_CLK_DSIA 48 +/* 49 */ +#define TEGRA124_CLK_MIPI 50 +#define TEGRA124_CLK_HDMI 51 +#define TEGRA124_CLK_CSI 52 +/* 53 */ +#define TEGRA124_CLK_I2C2 54 +#define TEGRA124_CLK_UARTC 55 +#define TEGRA124_CLK_MIPI_CAL 56 +#define TEGRA124_CLK_EMC 57 +#define TEGRA124_CLK_USB2 58 +#define TEGRA124_CLK_USB3 59 +/* 60 */ +#define TEGRA124_CLK_VDE 61 +#define TEGRA124_CLK_BSEA 62 +#define TEGRA124_CLK_BSEV 63 + +/* 64 */ +#define TEGRA124_CLK_UARTD 65 +/* 66 */ +#define TEGRA124_CLK_I2C3 67 +#define TEGRA124_CLK_SBC4 68 +#define TEGRA124_CLK_SDMMC3 69 +#define TEGRA124_CLK_PCIE 70 +#define TEGRA124_CLK_OWR 71 +#define TEGRA124_CLK_AFI 72 +#define TEGRA124_CLK_CSITE 73 +/* 74 */ +/* 75 */ +#define TEGRA124_CLK_LA 76 +#define TEGRA124_CLK_TRACE 77 +#define TEGRA124_CLK_SOC_THERM 78 +#define TEGRA124_CLK_DTV 79 +/* 80 */ +#define TEGRA124_CLK_I2CSLOW 81 +#define TEGRA124_CLK_DSIB 82 +#define TEGRA124_CLK_TSEC 83 +/* 84 */ +/* 85 */ +/* 86 */ +/* 87 */ +/* 88 */ +#define TEGRA124_CLK_XUSB_HOST 89 +/* 90 */ +#define TEGRA124_CLK_MSENC 91 +#define TEGRA124_CLK_CSUS 92 +/* 93 */ +/* 94 */ +/* 95 (bit affects xusb_dev and xusb_dev_src) */ + +/* 96 */ +/* 97 */ +/* 98 */ +#define TEGRA124_CLK_MSELECT 99 +#define TEGRA124_CLK_TSENSOR 100 +#define TEGRA124_CLK_I2S3 101 +#define TEGRA124_CLK_I2S4 102 +#define TEGRA124_CLK_I2C4 103 +#define TEGRA124_CLK_SBC5 104 +#define TEGRA124_CLK_SBC6 105 +#define TEGRA124_CLK_D_AUDIO 106 +#define TEGRA124_CLK_APBIF 107 +#define TEGRA124_CLK_DAM0 108 +#define TEGRA124_CLK_DAM1 109 +#define TEGRA124_CLK_DAM2 110 +#define TEGRA124_CLK_HDA2CODEC_2X 111 +/* 112 */ +#define TEGRA124_CLK_AUDIO0_2X 113 +#define TEGRA124_CLK_AUDIO1_2X 114 +#define TEGRA124_CLK_AUDIO2_2X 115 +#define TEGRA124_CLK_AUDIO3_2X 116 +#define TEGRA124_CLK_AUDIO4_2X 117 +#define TEGRA124_CLK_SPDIF_2X 118 +#define TEGRA124_CLK_ACTMON 119 +#define TEGRA124_CLK_EXTERN1 120 +#define TEGRA124_CLK_EXTERN2 121 +#define TEGRA124_CLK_EXTERN3 122 +#define TEGRA124_CLK_SATA_OOB 123 +#define TEGRA124_CLK_SATA 124 +#define TEGRA124_CLK_HDA 125 +/* 126 */ +#define TEGRA124_CLK_SE 127 + +#define TEGRA124_CLK_HDA2HDMI 128 +#define TEGRA124_CLK_SATA_COLD 129 +/* 130 */ +/* 131 */ +/* 132 */ +/* 133 */ +/* 134 */ +/* 135 */ +/* 136 */ +/* 137 */ +/* 138 */ +/* 139 */ +/* 140 */ +/* 141 */ +/* 142 */ +/* 143 (bit affects xusb_falcon_src, xusb_fs_src, */ +/* xusb_host_src and xusb_ss_src) */ +#define TEGRA124_CLK_CILAB 144 +#define TEGRA124_CLK_CILCD 145 +#define TEGRA124_CLK_CILE 146 +#define TEGRA124_CLK_DSIALP 147 +#define TEGRA124_CLK_DSIBLP 148 +#define TEGRA124_CLK_ENTROPY 149 +#define TEGRA124_CLK_DDS 150 +/* 151 */ +#define TEGRA124_CLK_DP2 152 +#define TEGRA124_CLK_AMX 153 +#define TEGRA124_CLK_ADX 154 +/* 155 (bit affects dfll_ref and dfll_soc) */ +#define TEGRA124_CLK_XUSB_SS 156 +/* 157 */ +/* 158 */ +/* 159 */ + +/* 160 */ +/* 161 */ +/* 162 */ +/* 163 */ +/* 164 */ +/* 165 */ +#define TEGRA124_CLK_I2C6 166 +/* 167 */ +/* 168 */ +/* 169 */ +/* 170 */ +#define TEGRA124_CLK_VIM2_CLK 171 +/* 172 */ +/* 173 */ +/* 174 */ +/* 175 */ +#define TEGRA124_CLK_HDMI_AUDIO 176 +#define TEGRA124_CLK_CLK72MHZ 177 +#define TEGRA124_CLK_VIC03 178 +/* 179 */ +#define TEGRA124_CLK_ADX1 180 +#define TEGRA124_CLK_DPAUX 181 +#define TEGRA124_CLK_SOR0 182 +/* 183 */ +#define TEGRA124_CLK_GPU 184 +#define TEGRA124_CLK_AMX1 185 +/* 186 */ +/* 187 */ +/* 188 */ +/* 189 */ +/* 190 */ +/* 191 */ +#define TEGRA124_CLK_UARTB 192 +#define TEGRA124_CLK_VFIR 193 +#define TEGRA124_CLK_SPDIF_IN 194 +#define TEGRA124_CLK_SPDIF_OUT 195 +#define TEGRA124_CLK_VI 196 +#define TEGRA124_CLK_VI_SENSOR 197 +#define TEGRA124_CLK_FUSE 198 +#define TEGRA124_CLK_FUSE_BURN 199 +#define TEGRA124_CLK_CLK_32K 200 +#define TEGRA124_CLK_CLK_M 201 +#define TEGRA124_CLK_CLK_M_DIV2 202 +#define TEGRA124_CLK_CLK_M_DIV4 203 +#define TEGRA124_CLK_PLL_REF 204 +#define TEGRA124_CLK_PLL_C 205 +#define TEGRA124_CLK_PLL_C_OUT1 206 +#define TEGRA124_CLK_PLL_C2 207 +#define TEGRA124_CLK_PLL_C3 208 +#define TEGRA124_CLK_PLL_M 209 +#define TEGRA124_CLK_PLL_M_OUT1 210 +#define TEGRA124_CLK_PLL_P 211 +#define TEGRA124_CLK_PLL_P_OUT1 212 +#define TEGRA124_CLK_PLL_P_OUT2 213 +#define TEGRA124_CLK_PLL_P_OUT3 214 +#define TEGRA124_CLK_PLL_P_OUT4 215 +#define TEGRA124_CLK_PLL_A 216 +#define TEGRA124_CLK_PLL_A_OUT0 217 +#define TEGRA124_CLK_PLL_D 218 +#define TEGRA124_CLK_PLL_D_OUT0 219 +#define TEGRA124_CLK_PLL_D2 220 +#define TEGRA124_CLK_PLL_D2_OUT0 221 +#define TEGRA124_CLK_PLL_U 222 +#define TEGRA124_CLK_PLL_U_480M 223 + +#define TEGRA124_CLK_PLL_U_60M 224 +#define TEGRA124_CLK_PLL_U_48M 225 +#define TEGRA124_CLK_PLL_U_12M 226 +#define TEGRA124_CLK_PLL_X 227 +#define TEGRA124_CLK_PLL_X_OUT0 228 +#define TEGRA124_CLK_PLL_RE_VCO 229 +#define TEGRA124_CLK_PLL_RE_OUT 230 +#define TEGRA124_CLK_PLL_E 231 +#define TEGRA124_CLK_SPDIF_IN_SYNC 232 +#define TEGRA124_CLK_I2S0_SYNC 233 +#define TEGRA124_CLK_I2S1_SYNC 234 +#define TEGRA124_CLK_I2S2_SYNC 235 +#define TEGRA124_CLK_I2S3_SYNC 236 +#define TEGRA124_CLK_I2S4_SYNC 237 +#define TEGRA124_CLK_VIMCLK_SYNC 238 +#define TEGRA124_CLK_AUDIO0 239 +#define TEGRA124_CLK_AUDIO1 240 +#define TEGRA124_CLK_AUDIO2 241 +#define TEGRA124_CLK_AUDIO3 242 +#define TEGRA124_CLK_AUDIO4 243 +#define TEGRA124_CLK_SPDIF 244 +#define TEGRA124_CLK_CLK_OUT_1 245 +#define TEGRA124_CLK_CLK_OUT_2 246 +#define TEGRA124_CLK_CLK_OUT_3 247 +#define TEGRA124_CLK_BLINK 248 +/* 249 */ +/* 250 */ +/* 251 */ +#define TEGRA124_CLK_XUSB_HOST_SRC 252 +#define TEGRA124_CLK_XUSB_FALCON_SRC 253 +#define TEGRA124_CLK_XUSB_FS_SRC 254 +#define TEGRA124_CLK_XUSB_SS_SRC 255 + +#define TEGRA124_CLK_XUSB_DEV_SRC 256 +#define TEGRA124_CLK_XUSB_DEV 257 +#define TEGRA124_CLK_XUSB_HS_SRC 258 +#define TEGRA124_CLK_SCLK 259 +#define TEGRA124_CLK_HCLK 260 +#define TEGRA124_CLK_PCLK 261 +#define TEGRA124_CLK_CCLK_G 262 +#define TEGRA124_CLK_CCLK_LP 263 +#define TEGRA124_CLK_DFLL_REF 264 +#define TEGRA124_CLK_DFLL_SOC 265 +#define TEGRA124_CLK_VI_SENSOR2 266 +#define TEGRA124_CLK_PLL_P_OUT5 267 +#define TEGRA124_CLK_CML0 268 +#define TEGRA124_CLK_CML1 269 +#define TEGRA124_CLK_PLL_C4 270 +#define TEGRA124_CLK_PLL_DP 271 +#define TEGRA124_CLK_PLL_E_MUX 272 +/* 273 */ +/* 274 */ +/* 275 */ +/* 276 */ +/* 277 */ +/* 278 */ +/* 279 */ +/* 280 */ +/* 281 */ +/* 282 */ +/* 283 */ +/* 284 */ +/* 285 */ +/* 286 */ +/* 287 */ + +/* 288 */ +/* 289 */ +/* 290 */ +/* 291 */ +/* 292 */ +/* 293 */ +/* 294 */ +/* 295 */ +/* 296 */ +/* 297 */ +/* 298 */ +/* 299 */ +#define TEGRA124_CLK_AUDIO0_MUX 300 +#define TEGRA124_CLK_AUDIO1_MUX 301 +#define TEGRA124_CLK_AUDIO2_MUX 302 +#define TEGRA124_CLK_AUDIO3_MUX 303 +#define TEGRA124_CLK_AUDIO4_MUX 304 +#define TEGRA124_CLK_SPDIF_MUX 305 +#define TEGRA124_CLK_CLK_OUT_1_MUX 306 +#define TEGRA124_CLK_CLK_OUT_2_MUX 307 +#define TEGRA124_CLK_CLK_OUT_3_MUX 308 +#define TEGRA124_CLK_DSIA_MUX 309 +#define TEGRA124_CLK_DSIB_MUX 310 +#define TEGRA124_CLK_SOR0_LVDS 311 +#define TEGRA124_CLK_CLK_MAX 312 + +#endif /* _DT_BINDINGS_CLOCK_TEGRA124_CAR_H */

From: Thierry Reding treding@nvidia.com
The Jetson TK1 has an ethernet NIC connected to the PCIe bus and routes the second root port to a miniPCIe slot. Enable the PCIe controller and the network driver to allow the device to boot over the network.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - move AS3722 PMIC code into separate driver - use revised list of supplies in DTS
arch/arm/dts/tegra124-jetson-tk1.dts | 347 +++++++++++++++++++++++++++++++++++ board/nvidia/jetson-tk1/jetson-tk1.c | 52 ++++++ include/configs/jetson-tk1.h | 13 ++ 3 files changed, 412 insertions(+)
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts index f61736f0ef0f..80106ec95ea3 100644 --- a/arch/arm/dts/tegra124-jetson-tk1.dts +++ b/arch/arm/dts/tegra124-jetson-tk1.dts @@ -26,6 +26,26 @@ reg = <0x80000000 0x80000000>; };
+ pcie-controller@01003000 { + status = "okay"; + + avddio-pex-supply = <&vdd_1v05_run>; + dvddio-pex-supply = <&vdd_1v05_run>; + avdd-pex-pll-supply = <&vdd_1v05_run>; + hvdd-pex-supply = <&vdd_3v3_lp0>; + hvdd-pex-pll-e-supply = <&vdd_3v3_lp0>; + vddio-pex-ctl-supply = <&vdd_3v3_lp0>; + avdd-pll-erefe-supply = <&avdd_1v05_run>; + + pci@1,0 { + status = "okay"; + }; + + pci@2,0 { + status = "okay"; + }; + }; + i2c@7000c000 { status = "okay"; clock-frequency = <100000>; @@ -46,9 +66,195 @@ clock-frequency = <100000>; };
+ /* Expansion PWR_I2C_*, on-board components */ i2c@7000d000 { status = "okay"; clock-frequency = <400000>; + + pmic: pmic@40 { + compatible = "ams,as3722"; + reg = <0x40>; + interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>; + + ams,system-power-controller; + + #interrupt-cells = <2>; + interrupt-controller; + + gpio-controller; + #gpio-cells = <2>; + + pinctrl-names = "default"; + pinctrl-0 = <&as3722_default>; + + as3722_default: pinmux { + gpio0 { + pins = "gpio0"; + function = "gpio"; + bias-pull-down; + }; + + gpio1_2_4_7 { + pins = "gpio1", "gpio2", "gpio4", "gpio7"; + function = "gpio"; + bias-pull-up; + }; + + gpio3_5_6 { + pins = "gpio3", "gpio5", "gpio6"; + bias-high-impedance; + }; + }; + + regulators { + vsup-sd2-supply = <&vdd_5v0_sys>; + vsup-sd3-supply = <&vdd_5v0_sys>; + vsup-sd4-supply = <&vdd_5v0_sys>; + vsup-sd5-supply = <&vdd_5v0_sys>; + vin-ldo0-supply = <&vdd_1v35_lp0>; + vin-ldo1-6-supply = <&vdd_3v3_run>; + vin-ldo2-5-7-supply = <&vddio_1v8>; + vin-ldo3-4-supply = <&vdd_3v3_sys>; + vin-ldo9-10-supply = <&vdd_5v0_sys>; + vin-ldo11-supply = <&vdd_3v3_run>; + + sd0 { + regulator-name = "+VDD_CPU_AP"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1400000>; + regulator-min-microamp = <3500000>; + regulator-max-microamp = <3500000>; + regulator-always-on; + regulator-boot-on; + ams,ext-control = <2>; + }; + + sd1 { + regulator-name = "+VDD_CORE"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1350000>; + regulator-min-microamp = <2500000>; + regulator-max-microamp = <2500000>; + regulator-always-on; + regulator-boot-on; + ams,ext-control = <1>; + }; + + vdd_1v35_lp0: sd2 { + regulator-name = "+1.35V_LP0(sd2)"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + sd3 { + regulator-name = "+1.35V_LP0(sd3)"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_1v05_run: sd4 { + regulator-name = "+1.05V_RUN"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + vddio_1v8: sd5 { + regulator-name = "+1.8V_VDDIO"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_gpu: sd6 { + regulator-name = "+VDD_GPU_AP"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1200000>; + regulator-min-microamp = <3500000>; + regulator-max-microamp = <3500000>; + regulator-boot-on; + regulator-always-on; + }; + + avdd_1v05_run: ldo0 { + regulator-name = "+1.05V_RUN_AVDD"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-boot-on; + regulator-always-on; + ams,ext-control = <1>; + }; + + ldo1 { + regulator-name = "+1.8V_RUN_CAM"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo2 { + regulator-name = "+1.2V_GEN_AVDD"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo3 { + regulator-name = "+1.05V_LP0_VDD_RTC"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-boot-on; + regulator-always-on; + ams,enable-tracking; + }; + + ldo4 { + regulator-name = "+2.8V_RUN_CAM"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo5 { + regulator-name = "+1.2V_RUN_CAM_FRONT"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + vddio_sdmmc3: ldo6 { + regulator-name = "+VDDIO_SDMMC3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + ldo7 { + regulator-name = "+1.05V_RUN_CAM_REAR"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + ldo9 { + regulator-name = "+3.3V_RUN_TOUCH"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo10 { + regulator-name = "+2.8V_RUN_CAM_AF"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo11 { + regulator-name = "+1.8V_RUN_VPP_FUSE"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + }; };
i2c@7000d100 { @@ -114,4 +320,145 @@ status = "okay"; nvidia,vbus-gpio = <&gpio 109 0>; /* gpio PN5, USB_VBUS_EN1 */ }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_mux: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "+VDD_MUX"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_5v0_sys: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "+5V_SYS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vdd_mux>; + }; + + vdd_3v3_sys: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "+3.3V_SYS"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vdd_mux>; + }; + + vdd_3v3_run: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + regulator-name = "+3.3V_RUN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_sys>; + }; + + vdd_3v3_hdmi: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "+3.3V_AVDD_HDMI_AP_GATED"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vdd_3v3_run>; + }; + + vdd_usb1_vbus: regulator@7 { + compatible = "regulator-fixed"; + reg = <7>; + regulator-name = "+USB0_VBUS_SW"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(N, 4) GPIO_ACTIVE_HIGH>; + enable-active-high; + gpio-open-drain; + vin-supply = <&vdd_5v0_sys>; + }; + + vdd_usb3_vbus: regulator@8 { + compatible = "regulator-fixed"; + reg = <8>; + regulator-name = "+5V_USB_HS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(N, 5) GPIO_ACTIVE_HIGH>; + enable-active-high; + gpio-open-drain; + vin-supply = <&vdd_5v0_sys>; + }; + + vdd_3v3_lp0: regulator@10 { + compatible = "regulator-fixed"; + reg = <10>; + regulator-name = "+3.3V_LP0"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 2 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_sys>; + }; + + vdd_hdmi_pll: regulator@11 { + compatible = "regulator-fixed"; + reg = <11>; + regulator-name = "+1.05V_RUN_AVDD_HDMI_PLL"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + gpio = <&gpio TEGRA_GPIO(H, 7) GPIO_ACTIVE_LOW>; + vin-supply = <&vdd_1v05_run>; + }; + + vdd_5v0_hdmi: regulator@12 { + compatible = "regulator-fixed"; + reg = <12>; + regulator-name = "+5V_HDMI_CON"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(K, 6) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_sys>; + }; + + /* Molex power connector */ + vdd_5v0_sata: regulator@13 { + compatible = "regulator-fixed"; + reg = <13>; + regulator-name = "+5V_SATA"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(EE, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_sys>; + }; + + vdd_12v0_sata: regulator@14 { + compatible = "regulator-fixed"; + reg = <14>; + regulator-name = "+12V_SATA"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + gpio = <&gpio TEGRA_GPIO(EE, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_mux>; + }; + }; }; diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c b/board/nvidia/jetson-tk1/jetson-tk1.c index 5d37718f3b89..263aa423dd04 100644 --- a/board/nvidia/jetson-tk1/jetson-tk1.c +++ b/board/nvidia/jetson-tk1/jetson-tk1.c @@ -6,10 +6,16 @@ */
#include <common.h> +#include <netdev.h> +#include <power/as3722.h> + #include <asm/arch/gpio.h> #include <asm/arch/pinmux.h> + #include "pinmux-config-jetson-tk1.h"
+DECLARE_GLOBAL_DATA_PTR; + /* * Routine: pinmux_init * Description: Do individual peripheral pinmux configs @@ -27,3 +33,49 @@ void pinmux_init(void) pinmux_config_drvgrp_table(jetson_tk1_drvgrps, ARRAY_SIZE(jetson_tk1_drvgrps)); } + +#ifdef CONFIG_PCI_TEGRA +int tegra_pcie_board_init(void) +{ + struct as3722 *pmic; + int err; + + err = as3722_init(&pmic, gd->fdt_blob); + if (err) { + error("failed to initialize AS3722 PMIC: %d\n", err); + return err; + } + + err = as3722_sd_enable(pmic, 4); + if (err < 0) { + error("failed to enable SD4: %d\n", err); + return err; + } + + err = as3722_sd_set_voltage(pmic, 4, 0x24); + if (err < 0) { + error("failed to set SD4 voltage: %d\n", err); + return err; + } + + err = as3722_gpio_configure(pmic, 1, AS3722_GPIO_OUTPUT_VDDH | + AS3722_GPIO_INVERT); + if (err < 0) { + error("failed to configure GPIO#1 as output: %d\n", err); + return err; + } + + err = as3722_gpio_direction_output(pmic, 2, 1); + if (err < 0) { + error("failed to set GPIO#2 high: %d\n", err); + return err; + } + + return 0; +} + +int board_eth_init(bd_t *bis) +{ + return pci_eth_init(bis); +} +#endif /* PCI */ diff --git a/include/configs/jetson-tk1.h b/include/configs/jetson-tk1.h index 0b9e5b699fa6..255f0f0a823e 100644 --- a/include/configs/jetson-tk1.h +++ b/include/configs/jetson-tk1.h @@ -10,6 +10,9 @@
#include <linux/sizes.h>
+/* enable PMIC */ +#define CONFIG_AS3722_POWER + #include "tegra124-common.h"
/* Enable fdt support for Jetson TK1. Flash the image in u-boot-dtb.bin */ @@ -71,6 +74,16 @@ #define CONFIG_USB_HOST_ETHER #define CONFIG_USB_ETHER_ASIX
+/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_TEGRA +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI +#define CONFIG_CMD_PCI_ENUM + +/* PCI networking support */ +#define CONFIG_RTL8169 + /* General networking support */ #define CONFIG_CMD_NET #define CONFIG_CMD_DHCP

From: Thierry Reding treding@nvidia.com
Remove two gratuituous blank lines, uses u32 (instead of int) as the type for values that will be written to a register, moves the beginning of the variable declaration section to a separate line (rather than the one with the opening brace) and keeps the function signature on a single line where possible.
Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/cpu/armv7/cache_v7.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c index a2c4032fed8c..0f9d8377ed5a 100644 --- a/arch/arm/cpu/armv7/cache_v7.c +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -21,7 +21,8 @@ * to get size details from Current Cache Size ID Register(CCSIDR) */ static void set_csselr(u32 level, u32 type) -{ u32 csselr = level << 1 | type; +{ + u32 csselr = level << 1 | type;
/* Write to Cache Size Selection Register(CSSELR) */ asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); @@ -49,7 +50,8 @@ static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, u32 num_ways, u32 way_shift, u32 log2_line_len) { - int way, set, setway; + int way, set; + u32 setway;
/* * For optimal assembly code: @@ -73,7 +75,8 @@ static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, u32 num_ways, u32 way_shift, u32 log2_line_len) { - int way, set, setway; + int way, set; + u32 setway;
/* * For optimal assembly code: @@ -134,7 +137,6 @@ static void v7_maint_dcache_level_setway(u32 level, u32 operation) static void v7_maint_dcache_all(u32 operation) { u32 level, cache_type, level_start_bit = 0; - u32 clidr = get_clidr();
for (level = 0; level < 7; level++) { @@ -147,8 +149,7 @@ static void v7_maint_dcache_all(u32 operation) } }
-static void v7_dcache_clean_inval_range(u32 start, - u32 stop, u32 line_len) +static void v7_dcache_clean_inval_range(u32 start, u32 stop, u32 line_len) { u32 mva;
@@ -256,7 +257,6 @@ void flush_dcache_all(void) */ void invalidate_dcache_range(unsigned long start, unsigned long stop) { - v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE);
v7_outer_cache_inval_range(start, stop);

On 26 August 2014 09:34, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Remove two gratuituous blank lines, uses u32 (instead of int) as the type for values that will be written to a register, moves the beginning of the variable declaration section to a separate line (rather than the one with the opening brace) and keeps the function signature on a single line where possible.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org

Hello Thierry,
On Tue, 26 Aug 2014 17:34:20 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Remove two gratuituous blank lines, uses u32 (instead of int) as the type for values that will be written to a register, moves the beginning of the variable declaration section to a separate line (rather than the one with the opening brace) and keeps the function signature on a single line where possible.
Signed-off-by: Thierry Reding treding@nvidia.com
arch/arm/cpu/armv7/cache_v7.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c index a2c4032fed8c..0f9d8377ed5a 100644 --- a/arch/arm/cpu/armv7/cache_v7.c +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -21,7 +21,8 @@
- to get size details from Current Cache Size ID Register(CCSIDR)
*/ static void set_csselr(u32 level, u32 type) -{ u32 csselr = level << 1 | type; +{
u32 csselr = level << 1 | type;
/* Write to Cache Size Selection Register(CSSELR) */ asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
@@ -49,7 +50,8 @@ static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, u32 num_ways, u32 way_shift, u32 log2_line_len) {
- int way, set, setway;
int way, set;
u32 setway;
/*
- For optimal assembly code:
@@ -73,7 +75,8 @@ static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, u32 num_ways, u32 way_shift, u32 log2_line_len) {
- int way, set, setway;
int way, set;
u32 setway;
/*
- For optimal assembly code:
@@ -134,7 +137,6 @@ static void v7_maint_dcache_level_setway(u32 level, u32 operation) static void v7_maint_dcache_all(u32 operation) { u32 level, cache_type, level_start_bit = 0;
u32 clidr = get_clidr();
for (level = 0; level < 7; level++) {
@@ -147,8 +149,7 @@ static void v7_maint_dcache_all(u32 operation) } }
-static void v7_dcache_clean_inval_range(u32 start,
u32 stop, u32 line_len)
+static void v7_dcache_clean_inval_range(u32 start, u32 stop, u32 line_len) { u32 mva;
@@ -256,7 +257,6 @@ void flush_dcache_all(void) */ void invalidate_dcache_range(unsigned long start, unsigned long stop) {
v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE);
v7_outer_cache_inval_range(start, stop);
-- 2.0.4
Applied to u-boot-arm/master, thanks!
Amicalement,

From: Thierry Reding treding@nvidia.com
size_t is the canonical type to represent variables that contain a size. Use it instead of signed integer. Physical addresses can be larger than 32-bit, so use a more appropriate type for them as well. phys_addr_t is a type that is 32-bit on systems that use 32-bit addresses and 64-bit if the system is 64-bit or uses a form of physical address extension to use a larger address space on 32-bit systems. Using these types the same API can be implemented on a wider range of systems.
Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/include/asm/system.h | 2 +- arch/arm/lib/cache-cp15.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index d51ba668f323..fb31a8faf2b1 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -200,7 +200,7 @@ enum { * \param size size of memory region to change * \param option dcache option to select */ -void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option);
/** diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c index 5fdfdbfca541..e6c1c83e5758 100644 --- a/arch/arm/lib/cache-cp15.c +++ b/arch/arm/lib/cache-cp15.c @@ -47,15 +47,15 @@ __weak void mmu_page_table_flush(unsigned long start, unsigned long stop) debug("%s: Warning: not implemented\n", __func__); }
-void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option) { u32 *page_table = (u32 *)gd->arch.tlb_addr; - u32 upto, end; + unsigned long upto, end;
end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; start = start >> MMU_SECTION_SHIFT; - debug("%s: start=%x, size=%x, option=%d\n", __func__, start, size, + debug("%s: start=%pa, size=%zu, option=%d\n", __func__, &start, size, option); for (upto = start; upto < end; upto++) set_section_dcache(upto, option);

Hi Thierry,
On 26 August 2014 09:34, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
size_t is the canonical type to represent variables that contain a size. Use it instead of signed integer. Physical addresses can be larger than 32-bit, so use a more appropriate type for them as well. phys_addr_t is a type that is 32-bit on systems that use 32-bit addresses and 64-bit if the system is 64-bit or uses a form of physical address extension to use a larger address space on 32-bit systems. Using these types the same API can be implemented on a wider range of systems.
Signed-off-by: Thierry Reding treding@nvidia.com
arch/arm/include/asm/system.h | 2 +- arch/arm/lib/cache-cp15.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index d51ba668f323..fb31a8faf2b1 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -200,7 +200,7 @@ enum {
- \param size size of memory region to change
- \param option dcache option to select
*/ -void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option);
/** diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c index 5fdfdbfca541..e6c1c83e5758 100644 --- a/arch/arm/lib/cache-cp15.c +++ b/arch/arm/lib/cache-cp15.c @@ -47,15 +47,15 @@ __weak void mmu_page_table_flush(unsigned long start, unsigned long stop) debug("%s: Warning: not implemented\n", __func__); }
-void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option) { u32 *page_table = (u32 *)gd->arch.tlb_addr;
u32 upto, end;
unsigned long upto, end; end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; start = start >> MMU_SECTION_SHIFT;
debug("%s: start=%x, size=%x, option=%d\n", __func__, start, size,
debug("%s: start=%pa, size=%zu, option=%d\n", __func__, &start, size,
The &start seems counter-intuitive. Can it not just be 'start'?
option); for (upto = start; upto < end; upto++) set_section_dcache(upto, option);
-- 2.0.4
Regards, Simon

Hello Thierry,
On Tue, 26 Aug 2014 17:34:21 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
size_t is the canonical type to represent variables that contain a size. Use it instead of signed integer. Physical addresses can be larger than 32-bit, so use a more appropriate type for them as well. phys_addr_t is a type that is 32-bit on systems that use 32-bit addresses and 64-bit if the system is 64-bit or uses a form of physical address extension to use a larger address space on 32-bit systems. Using these types the same API can be implemented on a wider range of systems.
Signed-off-by: Thierry Reding treding@nvidia.com
arch/arm/include/asm/system.h | 2 +- arch/arm/lib/cache-cp15.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index d51ba668f323..fb31a8faf2b1 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -200,7 +200,7 @@ enum {
- \param size size of memory region to change
- \param option dcache option to select
*/ -void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option);
/** diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c index 5fdfdbfca541..e6c1c83e5758 100644 --- a/arch/arm/lib/cache-cp15.c +++ b/arch/arm/lib/cache-cp15.c @@ -47,15 +47,15 @@ __weak void mmu_page_table_flush(unsigned long start, unsigned long stop) debug("%s: Warning: not implemented\n", __func__); }
-void mmu_set_region_dcache_behaviour(u32 start, int size, +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option) { u32 *page_table = (u32 *)gd->arch.tlb_addr;
- u32 upto, end;
unsigned long upto, end;
end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; start = start >> MMU_SECTION_SHIFT;
- debug("%s: start=%x, size=%x, option=%d\n", __func__, start, size,
- debug("%s: start=%pa, size=%zu, option=%d\n", __func__, &start, size, option); for (upto = start; upto < end; upto++) set_section_dcache(upto, option);
-- 2.0.4
Applied to u-boot-arm/master, thanks!
Amicalement,

From: Thierry Reding treding@nvidia.com
When DEBUG is set, output memory region used for malloc().
Signed-off-by: Thierry Reding treding@nvidia.com --- common/dlmalloc.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/common/dlmalloc.c b/common/dlmalloc.c index f9873393c183..3d6391e60acf 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -1533,6 +1533,9 @@ void mem_malloc_init(ulong start, ulong size) mem_malloc_end = start + size; mem_malloc_brk = start;
+ debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start, + mem_malloc_end); + memset((void *)mem_malloc_start, 0, size);
malloc_bin_reloc();

On 26 August 2014 09:34, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
When DEBUG is set, output memory region used for malloc().
Signed-off-by: Thierry Reding treding@nvidia.com
common/dlmalloc.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/common/dlmalloc.c b/common/dlmalloc.c index f9873393c183..3d6391e60acf 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -1533,6 +1533,9 @@ void mem_malloc_init(ulong start, ulong size) mem_malloc_end = start + size; mem_malloc_brk = start;
debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start,
mem_malloc_end);
Acked-by: Simon Glass sjg@chromium.org
memset((void *)mem_malloc_start, 0, size); malloc_bin_reloc();
-- 2.0.4

Hello Thierry,
On Tue, 26 Aug 2014 17:34:22 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
When DEBUG is set, output memory region used for malloc().
Signed-off-by: Thierry Reding treding@nvidia.com
common/dlmalloc.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/common/dlmalloc.c b/common/dlmalloc.c index f9873393c183..3d6391e60acf 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -1533,6 +1533,9 @@ void mem_malloc_init(ulong start, ulong size) mem_malloc_end = start + size; mem_malloc_brk = start;
debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start,
mem_malloc_end);
memset((void *)mem_malloc_start, 0, size);
malloc_bin_reloc();
-- 2.0.4
Applied to u-boot-arm/master, thanks!
Amicalement,

From: Thierry Reding treding@nvidia.com
Implement an API that can be used by drivers to allocate memory from a pool that is mapped uncached. This is useful if drivers would otherwise need to do extensive cache maintenance (or explicitly maintaining the cache isn't safe).
The API is protected using the new CONFIG_SYS_NONCACHED_MEMORY setting. Boards can set this to the size to be used for the non-cached area. The area will typically be right below the malloc() area, but architectures should take care of aligning the beginning and end of the area to honor any mapping restrictions. Architectures must also ensure that mappings established for this area do not overlap with the malloc() area (which should remain cached for improved performance).
While the API is currently only implemented for ARM v7, it should be generic enough to allow other architectures to implement it as well.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - avoid overflow when checking for available space
README | 19 +++++++++++++++++++ arch/arm/include/asm/system.h | 5 +++++ arch/arm/lib/cache.c | 42 ++++++++++++++++++++++++++++++++++++++++++ common/board_r.c | 11 +++++++++++ 4 files changed, 77 insertions(+)
diff --git a/README b/README index 1d713596ec6f..526e06edb52c 100644 --- a/README +++ b/README @@ -3759,6 +3759,25 @@ Configuration Settings: Pre-relocation malloc() is only supported on ARM at present but is fairly easy to enable for other archs.
+- CONFIG_SYS_NONCACHED_MEMORY: + Size of non-cached memory area. This area of memory will be + typically located right below the malloc() area and mapped + uncached in the MMU. This is useful for drivers that would + otherwise require a lot of explicit cache maintenance. For + some drivers it's also impossible to properly maintain the + cache. For example if the regions that need to be flushed + are not a multiple of the cache-line size, *and* padding + cannot be allocated between the regions to align them (i.e. + if the HW requires a contiguous array of regions, and the + size of each region is not cache-aligned), then a flush of + one region may result in overwriting data that hardware has + written to another region in the same cache-line. This can + happen for example in network drivers where descriptors for + buffers are typically smaller than the CPU cache-line (e.g. + 16 bytes vs. 32 or 64 bytes). + + Non-cached memory is only supported on 32-bit ARM at present. + - CONFIG_SYS_BOOTM_LEN: Normally compressed uImages are limited to an uncompressed size of 8 MBytes. If this is not enough, diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index fb31a8faf2b1..fb1bc0699a56 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -211,6 +211,11 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, */ void mmu_page_table_flush(unsigned long start, unsigned long stop);
+#ifdef CONFIG_SYS_NONCACHED_MEMORY +void noncached_init(void); +phys_addr_t noncached_alloc(size_t size, size_t align); +#endif /* CONFIG_SYS_NONCACHED_MEMORY */ + #endif /* __ASSEMBLY__ */
#define arch_align_stack(x) (x) diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c index 4e597a4c1d16..b2aa7740d4f8 100644 --- a/arch/arm/lib/cache.c +++ b/arch/arm/lib/cache.c @@ -8,6 +8,7 @@ /* for now: just dummy functions to satisfy the linker */
#include <common.h> +#include <malloc.h>
__weak void flush_cache(unsigned long start, unsigned long size) { @@ -49,3 +50,44 @@ __weak void enable_caches(void) { puts("WARNING: Caches not enabled\n"); } + +#ifdef CONFIG_SYS_NONCACHED_MEMORY +/* + * Reserve one MMU section worth of address space below the malloc() area that + * will be mapped uncached. + */ +static unsigned long noncached_start; +static unsigned long noncached_end; +static unsigned long noncached_next; + +void noncached_init(void) +{ + phys_addr_t start, end; + size_t size; + + end = ALIGN(mem_malloc_start, MMU_SECTION_SIZE) - MMU_SECTION_SIZE; + size = ALIGN(CONFIG_SYS_NONCACHED_MEMORY, MMU_SECTION_SIZE); + start = end - size; + + debug("mapping memory %pa-%pa non-cached\n", &start, &end); + + noncached_start = start; + noncached_end = end; + noncached_next = start; + + mmu_set_region_dcache_behaviour(noncached_start, size, DCACHE_OFF); +} + +phys_addr_t noncached_alloc(size_t size, size_t align) +{ + phys_addr_t next = ALIGN(noncached_next, align); + + if (next >= noncached_end || (noncached_end - next) < size) + return 0; + + debug("allocated %zu bytes of uncached memory @%pa\n", size, &next); + noncached_next = next + size; + + return next; +} +#endif /* CONFIG_SYS_NONCACHED_MEMORY */ diff --git a/common/board_r.c b/common/board_r.c index ba9a68dc6691..6b8e62bf032b 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -270,6 +270,14 @@ static int initr_malloc(void) return 0; }
+#ifdef CONFIG_SYS_NONCACHED_MEMORY +static int initr_noncached(void) +{ + noncached_init(); + return 0; +} +#endif + #ifdef CONFIG_DM static int initr_dm(void) { @@ -765,6 +773,9 @@ init_fnc_t init_sequence_r[] = { #endif initr_barrier, initr_malloc, +#ifdef CONFIG_SYS_NONCACHED_MEMORY + initr_noncached, +#endif bootstage_relocate, #ifdef CONFIG_DM initr_dm,

Hi Thierry,
On 26 August 2014 09:34, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Implement an API that can be used by drivers to allocate memory from a pool that is mapped uncached. This is useful if drivers would otherwise need to do extensive cache maintenance (or explicitly maintaining the cache isn't safe).
The API is protected using the new CONFIG_SYS_NONCACHED_MEMORY setting. Boards can set this to the size to be used for the non-cached area. The area will typically be right below the malloc() area, but architectures should take care of aligning the beginning and end of the area to honor any mapping restrictions. Architectures must also ensure that mappings established for this area do not overlap with the malloc() area (which should remain cached for improved performance).
While the API is currently only implemented for ARM v7, it should be generic enough to allow other architectures to implement it as well.
Signed-off-by: Thierry Reding treding@nvidia.com
Changes in v2:
- avoid overflow when checking for available space
README | 19 +++++++++++++++++++ arch/arm/include/asm/system.h | 5 +++++ arch/arm/lib/cache.c | 42 ++++++++++++++++++++++++++++++++++++++++++ common/board_r.c | 11 +++++++++++ 4 files changed, 77 insertions(+)
diff --git a/README b/README index 1d713596ec6f..526e06edb52c 100644 --- a/README +++ b/README @@ -3759,6 +3759,25 @@ Configuration Settings: Pre-relocation malloc() is only supported on ARM at present but is fairly easy to enable for other archs.
+- CONFIG_SYS_NONCACHED_MEMORY:
Size of non-cached memory area. This area of memory will be
typically located right below the malloc() area and mapped
uncached in the MMU. This is useful for drivers that would
otherwise require a lot of explicit cache maintenance. For
some drivers it's also impossible to properly maintain the
cache. For example if the regions that need to be flushed
are not a multiple of the cache-line size, *and* padding
cannot be allocated between the regions to align them (i.e.
if the HW requires a contiguous array of regions, and the
size of each region is not cache-aligned), then a flush of
one region may result in overwriting data that hardware has
written to another region in the same cache-line. This can
happen for example in network drivers where descriptors for
buffers are typically smaller than the CPU cache-line (e.g.
16 bytes vs. 32 or 64 bytes).
Non-cached memory is only supported on 32-bit ARM at present.
Is this better than allocating 1MB with memalign() and then changing the MMU settings with something like set_section_dcache()?
- CONFIG_SYS_BOOTM_LEN: Normally compressed uImages are limited to an uncompressed size of 8 MBytes. If this is not enough,
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index fb31a8faf2b1..fb1bc0699a56 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -211,6 +211,11 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, */ void mmu_page_table_flush(unsigned long start, unsigned long stop);
+#ifdef CONFIG_SYS_NONCACHED_MEMORY +void noncached_init(void); +phys_addr_t noncached_alloc(size_t size, size_t align); +#endif /* CONFIG_SYS_NONCACHED_MEMORY */
#endif /* __ASSEMBLY__ */
#define arch_align_stack(x) (x) diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c index 4e597a4c1d16..b2aa7740d4f8 100644 --- a/arch/arm/lib/cache.c +++ b/arch/arm/lib/cache.c @@ -8,6 +8,7 @@ /* for now: just dummy functions to satisfy the linker */
#include <common.h> +#include <malloc.h>
__weak void flush_cache(unsigned long start, unsigned long size) { @@ -49,3 +50,44 @@ __weak void enable_caches(void) { puts("WARNING: Caches not enabled\n"); }
+#ifdef CONFIG_SYS_NONCACHED_MEMORY +/*
- Reserve one MMU section worth of address space below the malloc() area that
- will be mapped uncached.
It looks like it can be >1 section.
- */
+static unsigned long noncached_start; +static unsigned long noncached_end; +static unsigned long noncached_next;
You can use ulong if you like.
+void noncached_init(void) +{
phys_addr_t start, end;
size_t size;
end = ALIGN(mem_malloc_start, MMU_SECTION_SIZE) - MMU_SECTION_SIZE;
size = ALIGN(CONFIG_SYS_NONCACHED_MEMORY, MMU_SECTION_SIZE);
start = end - size;
debug("mapping memory %pa-%pa non-cached\n", &start, &end);
noncached_start = start;
noncached_end = end;
noncached_next = start;
mmu_set_region_dcache_behaviour(noncached_start, size, DCACHE_OFF);
+}
+phys_addr_t noncached_alloc(size_t size, size_t align) +{
phys_addr_t next = ALIGN(noncached_next, align);
if (next >= noncached_end || (noncached_end - next) < size)
return 0;
debug("allocated %zu bytes of uncached memory @%pa\n", size, &next);
noncached_next = next + size;
Should this be aligned? I suspect it doesn't matter, and you do it anyway on the next call.
return next;
+} +#endif /* CONFIG_SYS_NONCACHED_MEMORY */ diff --git a/common/board_r.c b/common/board_r.c index ba9a68dc6691..6b8e62bf032b 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -270,6 +270,14 @@ static int initr_malloc(void) return 0; }
+#ifdef CONFIG_SYS_NONCACHED_MEMORY +static int initr_noncached(void) +{
noncached_init();
Could you make this function return 0 to avoid needing this helper?
return 0;
+} +#endif
#ifdef CONFIG_DM static int initr_dm(void) { @@ -765,6 +773,9 @@ init_fnc_t init_sequence_r[] = { #endif initr_barrier, initr_malloc, +#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
+#endif bootstage_relocate, #ifdef CONFIG_DM initr_dm, -- 2.0.4
Regards, Simon

On 08/26/2014 09:34 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Implement an API that can be used by drivers to allocate memory from a pool that is mapped uncached. This is useful if drivers would otherwise need to do extensive cache maintenance (or explicitly maintaining the cache isn't safe).
The API is protected using the new CONFIG_SYS_NONCACHED_MEMORY setting. Boards can set this to the size to be used for the non-cached area. The area will typically be right below the malloc() area, but architectures should take care of aligning the beginning and end of the area to honor any mapping restrictions. Architectures must also ensure that mappings established for this area do not overlap with the malloc() area (which should remain cached for improved performance).
While the API is currently only implemented for ARM v7, it should be generic enough to allow other architectures to implement it as well.
diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c
+void noncached_init(void)
...
- mmu_set_region_dcache_behaviour(noncached_start, size, DCACHE_OFF);
+}
If I build with:
#define CONFIG_SYS_DCACHE_OFF #define CONFIG_SYS_ICACHE_OFF
... then mmu_set_region_dcache_behaviour() doesn't exist (or at least isn't linked in) on Jetson TK1 at least.

Hi Stephen,
On 24 October 2014 13:11, Stephen Warren swarren@wwwdotorg.org wrote:
On 08/26/2014 09:34 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Implement an API that can be used by drivers to allocate memory from a pool that is mapped uncached. This is useful if drivers would otherwise need to do extensive cache maintenance (or explicitly maintaining the cache isn't safe).
The API is protected using the new CONFIG_SYS_NONCACHED_MEMORY setting. Boards can set this to the size to be used for the non-cached area. The area will typically be right below the malloc() area, but architectures should take care of aligning the beginning and end of the area to honor any mapping restrictions. Architectures must also ensure that mappings established for this area do not overlap with the malloc() area (which should remain cached for improved performance).
While the API is currently only implemented for ARM v7, it should be generic enough to allow other architectures to implement it as well.
diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c
+void noncached_init(void)
...
mmu_set_region_dcache_behaviour(noncached_start, size,
DCACHE_OFF); +}
If I build with:
#define CONFIG_SYS_DCACHE_OFF #define CONFIG_SYS_ICACHE_OFF
... then mmu_set_region_dcache_behaviour() doesn't exist (or at least isn't linked in) on Jetson TK1 at least.
I see that too. If you #undef CONFIG_SYS_NONCACHED_MEMORY then the problem goes away. There is a warning in the network driver in this case but it is not important.
A better solution is probably an #ifdef around the call you mention. It minimised code changes when cache is on/off which is probably a good thing.
Regards Simon

From: Thierry Reding treding@nvidia.com
Some boards, most notably those with a PCIe ethernet NIC, require this to avoid cache coherency problems. Since the option adds very little code and overhead enable it across all Tegra generations. Other drivers may also start supporting this functionality at some point, so enabling it now will automatically reap the benefits later on.
Acked-by: Stephen Warren swarren@nvidia.com Signed-off-by: Thierry Reding treding@nvidia.com --- include/configs/tegra-common.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h index 717cd61bd69a..16f45f5def9b 100644 --- a/include/configs/tegra-common.h +++ b/include/configs/tegra-common.h @@ -41,6 +41,7 @@ * Size of malloc() pool */ #define CONFIG_SYS_MALLOC_LEN (4 << 20) /* 4MB */ +#define CONFIG_SYS_NONCACHED_MEMORY (1 << 20) /* 1 MiB */
/* * NS16550 Configuration

From: Thierry Reding treding@nvidia.com
According to the top-level README file, this configuration setting can be used to override the number of receive buffers that an ethernet NIC uses.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/net/rtl8169.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index d040ab171bf5..c3eb474f0fba 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -79,7 +79,11 @@ static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#ifdef CONFIG_SYS_RX_ETH_BUFFER + #define NUM_RX_DESC CONFIG_SYS_RX_ETH_BUFFER +#else + #define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#endif #define RX_BUF_SIZE 1536 /* Rx Buffer size */ #define RX_BUF_LEN 8192

From: Thierry Reding treding@nvidia.com
RX and TX descriptor rings should be aligned to 256 byte boundaries. Use the DEFINE_ALIGN_BUFFER() macro to define the buffers so that they don't have to be manually aligned later on. Also make sure that the buffers do align to cache-line boundaries in case the cache-line is higher than the 256 byte alignment requirements of the NIC.
Also add a warning if the cache-line size is larger than the descriptor size, because the driver may discard changes to descriptors made by the hardware when requeuing RX buffers.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - make cache-line vs. descriptor size a compile-time warning
drivers/net/rtl8169.c | 63 +++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index c3eb474f0fba..d81ee8af1cc1 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -277,23 +277,41 @@ struct RxDesc { u32 buf_Haddr; };
-/* Define the TX Descriptor */ -static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; -/* __attribute__ ((aligned(256))); */ +#define RTL8169_DESC_SIZE 16
-/* Create a static buffer of size RX_BUF_SZ for each -TX Descriptor. All descriptors point to a -part of this buffer */ -static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; +#if ARCH_DMA_MINALIGN > 256 +# define RTL8169_ALIGN ARCH_DMA_MINALIGN +#else +# define RTL8169_ALIGN 256 +#endif + +/* + * Warn if the cache-line size is larger than the descriptor size. In such + * cases the driver will likely fail because the CPU needs to flush the cache + * when requeuing RX buffers, therefore descriptors written by the hardware + * may be discarded. + */ +#if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#warning cache-line size is larger than descriptor size +#endif + +/* Define the TX Descriptor */ +DEFINE_ALIGN_BUFFER(struct TxDesc, tx_ring, NUM_TX_DESC, RTL8169_ALIGN);
/* Define the RX Descriptor */ -static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; - /* __attribute__ ((aligned(256))); */ +DEFINE_ALIGN_BUFFER(struct RxDesc, rx_ring, NUM_RX_DESC, RTL8169_ALIGN);
-/* Create a static buffer of size RX_BUF_SZ for each -RX Descriptor All descriptors point to a -part of this buffer */ -static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +/* + * Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); + +/* + * Create a static buffer of size RX_BUF_SZ for each RX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN);
struct rtl8169_private { void *mmio_addr; /* memory map physical address */ @@ -301,8 +319,6 @@ struct rtl8169_private { unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ unsigned char *RxBufferRings; /* Index of Rx Buffer */ @@ -710,16 +726,6 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif
- tpc->TxDescArrays = tx_ring; - /* Tx Desscriptor needs 256 bytes alignment; */ - tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + - 255) & ~255); - - tpc->RxDescArrays = rx_ring; - /* Rx Desscriptor needs 256 bytes alignment; */ - tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + - 255) & ~255); - rtl8169_init_ring(dev); rtl8169_hw_start(dev); /* Construct a perfect filter frame with the mac address as first match @@ -761,10 +767,6 @@ static void rtl_halt(struct eth_device *dev)
RTL_W32(RxMissed, 0);
- tpc->TxDescArrays = NULL; - tpc->RxDescArrays = NULL; - tpc->TxDescArray = NULL; - tpc->RxDescArray = NULL; for (i = 0; i < NUM_RX_DESC; i++) { tpc->RxBufferRing[i] = NULL; } @@ -909,6 +911,9 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif }
+ tpc->TxDescArray = tx_ring; + tpc->RxDescArray = rx_ring; + return 1; }

Hi,
I tested on sh7785lcr board with this patch. This worked without any problems. Thanks.
2014-08-27 0:34 GMT+09:00 Thierry Reding thierry.reding@gmail.com:
From: Thierry Reding treding@nvidia.com
RX and TX descriptor rings should be aligned to 256 byte boundaries. Use the DEFINE_ALIGN_BUFFER() macro to define the buffers so that they don't have to be manually aligned later on. Also make sure that the buffers do align to cache-line boundaries in case the cache-line is higher than the 256 byte alignment requirements of the NIC.
Also add a warning if the cache-line size is larger than the descriptor size, because the driver may discard changes to descriptors made by the hardware when requeuing RX buffers.
Signed-off-by: Thierry Reding treding@nvidia.com
Changes in v2:
- make cache-line vs. descriptor size a compile-time warning
Tested-by: Nobuhiro Iwamatsu nobuhiro.iwamatsu.yj@renesas.com
drivers/net/rtl8169.c | 63 +++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index c3eb474f0fba..d81ee8af1cc1 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -277,23 +277,41 @@ struct RxDesc { u32 buf_Haddr; };
-/* Define the TX Descriptor */ -static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; -/* __attribute__ ((aligned(256))); */ +#define RTL8169_DESC_SIZE 16
-/* Create a static buffer of size RX_BUF_SZ for each -TX Descriptor. All descriptors point to a -part of this buffer */ -static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; +#if ARCH_DMA_MINALIGN > 256 +# define RTL8169_ALIGN ARCH_DMA_MINALIGN +#else +# define RTL8169_ALIGN 256 +#endif
+/*
- Warn if the cache-line size is larger than the descriptor size. In such
- cases the driver will likely fail because the CPU needs to flush the cache
- when requeuing RX buffers, therefore descriptors written by the hardware
- may be discarded.
- */
+#if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#warning cache-line size is larger than descriptor size +#endif
+/* Define the TX Descriptor */ +DEFINE_ALIGN_BUFFER(struct TxDesc, tx_ring, NUM_TX_DESC, RTL8169_ALIGN);
/* Define the RX Descriptor */ -static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256];
- /* __attribute__ ((aligned(256))); */
+DEFINE_ALIGN_BUFFER(struct RxDesc, rx_ring, NUM_RX_DESC, RTL8169_ALIGN);
-/* Create a static buffer of size RX_BUF_SZ for each -RX Descriptor All descriptors point to a -part of this buffer */ -static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +/*
- Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All
- descriptors point to a part of this buffer.
- */
+DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN);
+/*
- Create a static buffer of size RX_BUF_SZ for each RX Descriptor. All
- descriptors point to a part of this buffer.
- */
+DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN);
struct rtl8169_private { void *mmio_addr; /* memory map physical address */ @@ -301,8 +319,6 @@ struct rtl8169_private { unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ unsigned long dirty_tx;
unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */
unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ unsigned char *RxBufferRings; /* Index of Rx Buffer */
@@ -710,16 +726,6 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif
tpc->TxDescArrays = tx_ring;
/* Tx Desscriptor needs 256 bytes alignment; */
tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays +
255) & ~255);
tpc->RxDescArrays = rx_ring;
/* Rx Desscriptor needs 256 bytes alignment; */
tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays +
255) & ~255);
rtl8169_init_ring(dev); rtl8169_hw_start(dev); /* Construct a perfect filter frame with the mac address as first match
@@ -761,10 +767,6 @@ static void rtl_halt(struct eth_device *dev)
RTL_W32(RxMissed, 0);
tpc->TxDescArrays = NULL;
tpc->RxDescArrays = NULL;
tpc->TxDescArray = NULL;
tpc->RxDescArray = NULL; for (i = 0; i < NUM_RX_DESC; i++) { tpc->RxBufferRing[i] = NULL; }
@@ -909,6 +911,9 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif }
tpc->TxDescArray = tx_ring;
tpc->RxDescArray = rx_ring;
return 1;
}
-- 2.0.4
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

From: Thierry Reding treding@nvidia.com
To work around potential issues with explicit cache maintenance of the RX and TX descriptor rings, allocate them from a pool of uncached memory if the architecture supports it.
Signed-off-by: Thierry Reding treding@nvidia.com --- Changes in v2: - if non-cached memory is not available, use memalign() for more consistent descriptor allocations - allow rtl_init() to fail
drivers/net/rtl8169.c | 71 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 11 deletions(-)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index d81ee8af1cc1..22ad70719937 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -41,6 +41,7 @@ * Modified to use le32_to_cpu and cpu_to_le32 properly */ #include <common.h> +#include <errno.h> #include <malloc.h> #include <net.h> #include <netdev.h> @@ -290,16 +291,15 @@ struct RxDesc { * cases the driver will likely fail because the CPU needs to flush the cache * when requeuing RX buffers, therefore descriptors written by the hardware * may be discarded. + * + * This can be fixed by defining CONFIG_SYS_NONCACHED_MEMORY which will cause + * the driver allocate descriptors from a pool of non-cached memory. */ #if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#ifndef CONFIG_SYS_NONCACHED_MEMORY #warning cache-line size is larger than descriptor size #endif - -/* Define the TX Descriptor */ -DEFINE_ALIGN_BUFFER(struct TxDesc, tx_ring, NUM_TX_DESC, RTL8169_ALIGN); - -/* Define the RX Descriptor */ -DEFINE_ALIGN_BUFFER(struct RxDesc, rx_ring, NUM_RX_DESC, RTL8169_ALIGN); +#endif
/* * Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All @@ -418,34 +418,71 @@ match: }
/* + * TX and RX descriptors are 16 bytes. This causes problems with the cache + * maintenance on CPUs where the cache-line size exceeds the size of these + * descriptors. What will happen is that when the driver receives a packet + * it will be immediately requeued for the hardware to reuse. The CPU will + * therefore need to flush the cache-line containing the descriptor, which + * will cause all other descriptors in the same cache-line to be flushed + * along with it. If one of those descriptors had been written to by the + * device those changes (and the associated packet) will be lost. + * + * To work around this, we make use of non-cached memory if available. If + * descriptors are mapped uncached there's no need to manually flush them + * or invalidate them. + * + * Note that this only applies to descriptors. The packet data buffers do + * not have the same constraints since they are 1536 bytes large, so they + * are unlikely to share cache-lines. + */ +static void *rtl_alloc_descs(unsigned int num) +{ + size_t size = num * RTL8169_DESC_SIZE; + +#ifdef CONFIG_SYS_NONCACHED_MEMORY + return (void *)noncached_alloc(size, RTL8169_ALIGN); +#else + return memalign(RTL8169_ALIGN, size); +#endif +} + +/* * Cache maintenance functions. These are simple wrappers around the more * general purpose flush_cache() and invalidate_dcache_range() functions. */
static void rtl_inval_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN);
invalidate_dcache_range(start, end); +#endif }
static void rtl_flush_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif }
static void rtl_inval_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN);
invalidate_dcache_range(start, end); +#endif }
static void rtl_flush_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif }
static void rtl_inval_buffer(void *buf, size_t size) @@ -911,10 +948,16 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif }
- tpc->TxDescArray = tx_ring; - tpc->RxDescArray = rx_ring;
- return 1; + tpc->RxDescArray = rtl_alloc_descs(NUM_RX_DESC); + if (!tpc->RxDescArray) + return -ENOMEM; + + tpc->TxDescArray = rtl_alloc_descs(NUM_TX_DESC); + if (!tpc->TxDescArray) + return -ENOMEM; + + return 0; }
int rtl8169_initialize(bd_t *bis) @@ -928,6 +971,7 @@ int rtl8169_initialize(bd_t *bis) while(1){ unsigned int region; u16 device; + int err;
/* Find RTL8169 */ if ((devno = pci_find_devices(supported, idx++)) < 0) @@ -966,9 +1010,14 @@ int rtl8169_initialize(bd_t *bis) dev->send = rtl_send; dev->recv = rtl_recv;
- eth_register (dev); + err = rtl_init(dev, bis); + if (err < 0) { + printf(pr_fmt("failed to initialize card: %d\n"), err); + free(dev); + continue; + }
- rtl_init(dev, bis); + eth_register (dev);
card_number++; }

From: Thierry Reding treding@nvidia.com
This network interface card in found on the NVIDIA Jetson TK1.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/net/rtl8169.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index 22ad70719937..8183df39e77a 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -253,6 +253,7 @@ static struct { {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, {"RTL-8168d/8111d", 0x28, 0xff7e1880,}, {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880,}, + {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, };

Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
Amicalement,

On 09/11/2014 10:00 AM, Albert ARIBAUD wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
Yes, applying it all in one place would probably make sense.
IIRC, Simon Glass already applied some of the DT patches early in the series? I CC'd him to check,
BTW, Thierry is on vacation at the moment, so I don't expect he'll respond.

Hi,
On 11 September 2014 10:17, Stephen Warren swarren@wwwdotorg.org wrote:
On 09/11/2014 10:00 AM, Albert ARIBAUD wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
Yes, applying it all in one place would probably make sense.
IIRC, Simon Glass already applied some of the DT patches early in the series? I CC'd him to check,
Yes I applied the fdt patches to u-boot-fdt/next. I can do a pull request to ARM if that helps, or you can just grab them.
BTW, Thierry is on vacation at the moment, so I don't expect he'll respond.
Regards, Simon

Hi Simon,
On Thu, 11 Sep 2014 13:20:03 -0600, Simon Glass sjg@chromium.org wrote:
Hi,
On 11 September 2014 10:17, Stephen Warren swarren@wwwdotorg.org wrote:
On 09/11/2014 10:00 AM, Albert ARIBAUD wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
Yes, applying it all in one place would probably make sense.
IIRC, Simon Glass already applied some of the DT patches early in the series? I CC'd him to check,
Yes I applied the fdt patches to u-boot-fdt/next. I can do a pull request to ARM if that helps, or you can just grab them.
Well, the idea was applying it all in one place :) and I don't like the idea of pulling the fdt tree into the arm tree, so I'll pick them up, but please give your Acked-by to them, so that PW sees it and puts it in when I apply the patches.
BTW, Thierry is on vacation at the moment, so I don't expect he'll respond.
Regards, Simon
Amicalement,

HI Albert,
On 18 September 2014 02:43, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Simon,
On Thu, 11 Sep 2014 13:20:03 -0600, Simon Glass sjg@chromium.org wrote:
Hi,
On 11 September 2014 10:17, Stephen Warren swarren@wwwdotorg.org
wrote:
On 09/11/2014 10:00 AM, Albert ARIBAUD wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including
myself).
It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
Yes, applying it all in one place would probably make sense.
IIRC, Simon Glass already applied some of the DT patches early in the series? I CC'd him to check,
Yes I applied the fdt patches to u-boot-fdt/next. I can do a pull request to ARM if that helps, or you can just grab them.
Well, the idea was applying it all in one place :) and I don't like the idea of pulling the fdt tree into the arm tree, so I'll pick them up, but please give your Acked-by to them, so that PW sees it and puts it in when I apply the patches.
They should all be acked already (I did that before I pulled them in) so please go ahead.
Regards, Simon

Hi,
On 11 September 2014 10:00, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
As this series still need some minor work, and the DT patches are separate, I have pushed them to u-boot-fdt/master and issued a pull request.
Thierry are you going to pick this up again soon? We should try to get this series applied in the next few weeks.
Regards, Simon

On Wed, Oct 22, 2014 at 09:07:40PM -0600, Simon Glass wrote:
Hi,
On 11 September 2014 10:00, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
As this series still need some minor work, and the DT patches are separate, I have pushed them to u-boot-fdt/master and issued a pull request.
Thierry are you going to pick this up again soon? We should try to get this series applied in the next few weeks.
I'm completely swamped right now, but I definitely plan on getting back to this at some point. But I can't really say for sure how soon that'll be.
Thierry

Hi Thierry,
On 23 October 2014 02:11, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Oct 22, 2014 at 09:07:40PM -0600, Simon Glass wrote:
Hi,
On 11 September 2014 10:00, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Thierry,
On Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Note: this series was split over several custodians (including myself). It might thus get applied piecewise... Shouldn't it rather be assigned a single custodian -with others giving their Ack) and be applied as a whole? And yes, I'm ok with getting all the pieces.
As this series still need some minor work, and the DT patches are separate, I have pushed them to u-boot-fdt/master and issued a pull request.
Thierry are you going to pick this up again soon? We should try to get this series applied in the next few weeks.
I'm completely swamped right now, but I definitely plan on getting back to this at some point. But I can't really say for sure how soon that'll be.
Since the changes are fairly trivial and I happen to have some of the platforms, I could respin if you like? Would be great to it into this merge window, and I have patches that it might affect.
Let me know what you think.
Regards, Simon

Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
FYI I have applied this series on top of driver model - it is at u-boot-dm.git branch 'norrin-working'. There were a quite few conflicts which I resolved (mostly with mainline I think). I tested it on beaver, trimslice and jetson-tk1.
Are you going to respin it?
Regards, Simon

On Sun, Sep 28, 2014 at 04:48:47PM -0600, Simon Glass wrote:
Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
FYI I have applied this series on top of driver model - it is at u-boot-dm.git branch 'norrin-working'. There were a quite few conflicts which I resolved (mostly with mainline I think). I tested it on beaver, trimslice and jetson-tk1.
Are you going to respin it?
I started looking at DM for I2C as Heiko suggested but it became too complex and time consuming, so what I'm planning to do is drop the I2C "enhancements" and use plain i2c_set_bus_num() and friends.
If there's any interest I can push a branch with the work-in-progress I2C DM patches, perhaps it could serve as the basis if someone else wanted to continue with that effort.
Thierry

Hi Thierry,
On 29 September 2014 02:11, Thierry Reding thierry.reding@gmail.com wrote:
On Sun, Sep 28, 2014 at 04:48:47PM -0600, Simon Glass wrote:
Hi Thierry,
On 26 August 2014 09:33, Thierry Reding thierry.reding@gmail.com
wrote:
From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The
size
is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
FYI I have applied this series on top of driver model - it is at u-boot-dm.git branch 'norrin-working'. There were a quite few conflicts which I resolved (mostly with mainline I think). I tested it on beaver, trimslice and jetson-tk1.
Are you going to respin it?
I started looking at DM for I2C as Heiko suggested but it became too complex and time consuming, so what I'm planning to do is drop the I2C "enhancements" and use plain i2c_set_bus_num() and friends.
OK. I looked at the series and you should really be using the pmic framework. I've pushed a branch to 'dm/norrin-working' that changes it (see the as3722 patch near the top). See tps65090 for an example. It should avoid any messing with I2C. Then, when the new PMIC framework rolls out your code will ride along for free. Plus you can drop the i2c additions that way.
Also, there is a clock function that seems to bash various registers - tegra_plle_enable. I think this should use clock_set_rate() - i.e. build in support for the new clock rather than creating a parallel function.
The power gate stuff looks good. Are you planning to replace the power gate code in arch/arm/cpu/arm720t/tegra124/cpu.c and the like? The only minor issue I see is the availability of get_timer() - but in tegra_powergate_set() 25ms seems like a very long delay to me anyway.
If there's any interest I can push a branch with the work-in-progress I2C DM patches, perhaps it could serve as the basis if someone else wanted to continue with that effort.
Yes I think that would be useful.
Regards, Simon

Hello Thierry,
Am 29.09.2014 10:11, schrieb Thierry Reding:
On Sun, Sep 28, 2014 at 04:48:47PM -0600, Simon Glass wrote:
Hi Thierry,
On 26 August 2014 09:33, Thierry Redingthierry.reding@gmail.com wrote:
From: Thierry Redingtreding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
FYI I have applied this series on top of driver model - it is at u-boot-dm.git branch 'norrin-working'. There were a quite few conflicts which I resolved (mostly with mainline I think). I tested it on beaver, trimslice and jetson-tk1.
Are you going to respin it?
I started looking at DM for I2C as Heiko suggested but it became too complex and time consuming, so what I'm planning to do is drop the I2C "enhancements" and use plain i2c_set_bus_num() and friends.
If there's any interest I can push a branch with the work-in-progress I2C DM patches, perhaps it could serve as the basis if someone else wanted to continue with that effort.
Yes please, maybe its worth to use it as a base ...
bye, Heiko

Le Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com a écrit :
From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
Patch 1 adds a %pa modifier to printf() to print physical addresses. This is required to allow code to output such addresses irrespective of whether a 64 bit or 32 bit architecture is built for.
Patches 2-7 add various FDT helpers to make it easier to parse complex device trees.
Patch 8 is a minor cleanup to the PCI command that prevents a spew of error messages if a bus does not exist. Patch 9 modifies the PCI enumeration code to honor theh restrictions encoded within a host controller driver's pci_ski_dev() implementation. This is required to prevent exceptions from the NVIDIA Tegra PCIe controller.
Patch 10 imports the pr_fmt() macro used within the Linux kernel to reduce the number of characters consumed by literal strings by allowing a source file to specify a prefix or suffix that should be applied to all format strings in the file. It is currently used by the debug() and error() macros.
Patches 11-14 are some cleanup and refactoring of I2C core code, the addition of a higher level API that makes it easier for I2C client drivers to talk to devices. The Tegra I2C driver now implements i2c_get_bus_num_fdt() to obtain bus numbers corresponding to a DT node.
Patch 15 implements a driver for the AS3722 PMIC used on the Venice2 and Jetson TK1 boards.
Patches 16-21 are preparatory work for the Tegra PCIe controller. They add missing clock driver functionality as well as drivers for the Tegra powergate and XUSB pad controller blocks.
Patch 22 adds the PCIe controller driver for Tegra20, Tegra30 and Tegra124.
Device tree nodes and configurations options to enable PCIe on the TrimSlice (Tegra20), Beaver, Cardhu (Tegra30) and Jetson TK1 (Tegra124) boards are added in patches 23-31.
Patches 32-35 implement non-cached memory support that will be used in the last batch of patches to implement more reliable packet transfers in the r8169 driver. Patch 36 enables non-cached memory support on Tegra.
Finally, patches 37-40 implement non-cached memory support and various fixes in the r8169 driver and add support for the revision of the NIC found on the Jetson TK1.
The above boards all have an ethernet NIC connected to PCIe, which is what I tested with.
Thierry Reding (40): vsprintf: Add modifier for phys_addr_t fdt: Add a function to count strings fdt: Add a function to get the index of a string fdt: Add functions to retrieve strings fdt: Add resource parsing functions fdt: Add a function to return PCI BDF triplet fdt: Add a subnodes iterator macro pci: Abort early if bus does not exist pci: Honour pci_skip_dev() Add pr_fmt() macro i2c: Initialize the correct bus i2c: Refactor adapter initialization i2c: Add high-level API i2c: tegra: Implement i2c_get_bus_num_fdt() power: Add AMS AS3722 PMIC support ARM: tegra: Implement tegra_plle_enable() ARM: tegra: Provide PCIEXCLK reset ID ARM: tegra: Implement powergate support ARM: tegra: Implement XUSB pad controller ARM: tegra: Add XUSB pad controller on Tegra124 ARM: tegra: Enable XUSB pad controller on Jetson TK1 pci: tegra: Add Tegra PCIe driver ARM: tegra: Add Tegra20 PCIe device tree node ARM: tegra: Enable PCIe on TrimSlice ARM: tegra: Add GIC for Tegra30 ARM: tegra: Add Tegra30 PCIe device tree node ARM: tegra: Enable PCIe on Cardhu ARM: tegra: Enable PCIe on Beaver ARM: tegra: Add GIC for Tegra124 ARM: tegra: Add Tegra124 PCIe device tree node ARM: tegra: Enable PCIe on Jetson TK1 ARM: cache_v7: Various minor cleanups ARM: cache-cp15: Use more accurate types malloc: Output region when debugging ARM: Implement non-cached memory support ARM: tegra: Enable non-cached memory net: rtl8169: Honor CONFIG_SYS_RX_ETH_BUFFER net: rtl8169: Properly align buffers net: rtl8169: Use non-cached memory if available net: rtl8169: Add support for RTL-8168/8111g
README | 19 + arch/arm/cpu/armv7/cache_v7.c | 14 +- arch/arm/cpu/tegra-common/Makefile | 2 + arch/arm/cpu/tegra-common/powergate.c | 102 ++ arch/arm/cpu/tegra-common/xusb-padctl.c | 39 + arch/arm/cpu/tegra124-common/Makefile | 1 + arch/arm/cpu/tegra124-common/clock.c | 109 +++ arch/arm/cpu/tegra124-common/xusb-padctl.c | 716 ++++++++++++++ arch/arm/cpu/tegra20-common/clock.c | 141 ++- arch/arm/cpu/tegra30-common/clock.c | 155 +++ arch/arm/dts/tegra124-jetson-tk1.dts | 373 +++++++ arch/arm/dts/tegra124.dtsi | 90 ++ arch/arm/dts/tegra20-trimslice.dts | 69 ++ arch/arm/dts/tegra20.dtsi | 60 ++ arch/arm/dts/tegra30-beaver.dts | 245 +++++ arch/arm/dts/tegra30-cardhu.dts | 362 +++++++ arch/arm/dts/tegra30.dtsi | 84 ++ arch/arm/include/asm/arch-tegra/powergate.h | 38 + arch/arm/include/asm/arch-tegra/xusb-padctl.h | 24 + arch/arm/include/asm/arch-tegra114/powergate.h | 6 + arch/arm/include/asm/arch-tegra124/clock.h | 2 + arch/arm/include/asm/arch-tegra124/powergate.h | 6 + arch/arm/include/asm/arch-tegra20/clock-tables.h | 2 +- arch/arm/include/asm/arch-tegra20/clock.h | 2 + arch/arm/include/asm/arch-tegra20/powergate.h | 6 + arch/arm/include/asm/arch-tegra30/clock.h | 2 + arch/arm/include/asm/arch-tegra30/powergate.h | 6 + arch/arm/include/asm/system.h | 7 +- arch/arm/lib/cache-cp15.c | 6 +- arch/arm/lib/cache.c | 42 + board/compulab/trimslice/trimslice.c | 8 + board/nvidia/cardhu/cardhu.c | 56 ++ board/nvidia/common/board.c | 3 + board/nvidia/jetson-tk1/jetson-tk1.c | 52 + common/board_r.c | 11 + common/cmd_pci.c | 7 + common/dlmalloc.c | 3 + drivers/i2c/i2c_core.c | 80 +- drivers/i2c/tegra_i2c.c | 13 + drivers/net/rtl8169.c | 127 ++- drivers/pci/Makefile | 1 + drivers/pci/pci.c | 3 + drivers/pci/pci_tegra.c | 1143 ++++++++++++++++++++++ drivers/power/Makefile | 1 + drivers/power/as3722.c | 300 ++++++ include/common.h | 14 +- include/configs/beaver.h | 10 + include/configs/cardhu.h | 10 + include/configs/jetson-tk1.h | 13 + include/configs/tegra-common.h | 1 + include/configs/trimslice.h | 10 + include/dt-bindings/clock/tegra124-car.h | 341 +++++++ include/dt-bindings/clock/tegra20-car.h | 158 +++ include/dt-bindings/clock/tegra30-car.h | 265 +++++ include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 + include/fdtdec.h | 69 ++ include/i2c.h | 96 ++ include/libfdt.h | 72 ++ include/pci.h | 1 + include/power/as3722.h | 27 + lib/fdtdec.c | 76 ++ lib/libfdt/fdt_ro.c | 76 ++ lib/vsprintf.c | 16 +- 63 files changed, 5736 insertions(+), 64 deletions(-) create mode 100644 arch/arm/cpu/tegra-common/powergate.c create mode 100644 arch/arm/cpu/tegra-common/xusb-padctl.c create mode 100644 arch/arm/cpu/tegra124-common/xusb-padctl.c create mode 100644 arch/arm/include/asm/arch-tegra/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra114/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra124/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra20/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra30/powergate.h create mode 100644 drivers/pci/pci_tegra.c create mode 100644 drivers/power/as3722.c create mode 100644 include/dt-bindings/clock/tegra124-car.h create mode 100644 include/dt-bindings/clock/tegra20-car.h create mode 100644 include/dt-bindings/clock/tegra30-car.h create mode 100644 include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h create mode 100644 include/power/as3722.h
Am I right in thinking there will be a V3 series, at least of the part not already awaiting upstream ? If so, maybe it should all be delegated to a single custodian -- right now it is split over five custodians (although part of it -- so that all of it is applied in one go without "foreign" patches getting in between.
Amicalement,

On Sun, 26 Oct 2014 20:07:10 +0100, Albert ARIBAUD albert.u.boot@aribaud.net a écrit :
delegated to a single custodian -- right now it is split over five custodians (although part of it -- so that all of it is applied in
Sorry for the "(although part of it" which I should have edited out.
Amicalement,

Hi Albert,
On 26 October 2014 13:07, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Le Tue, 26 Aug 2014 17:33:48 +0200, Thierry Reding thierry.reding@gmail.com a écrit :
From: Thierry Reding treding@nvidia.com
This series adds PCIe support for Tegra20, Tegra30 and Tegra124. The size is mostly due to the large number of infrastructure that's added (libfdt, Tegra specific drivers required by the PCIe driver). In this version I've included all patches that were previously split into three separate series. Spreading them over multiple series is probably not worth it since there might be some dependencies between them and only the end result gives a completely working setup.
Patch 1 adds a %pa modifier to printf() to print physical addresses. This is required to allow code to output such addresses irrespective of whether a 64 bit or 32 bit architecture is built for.
Patches 2-7 add various FDT helpers to make it easier to parse complex device trees.
Patch 8 is a minor cleanup to the PCI command that prevents a spew of error messages if a bus does not exist. Patch 9 modifies the PCI enumeration code to honor theh restrictions encoded within a host controller driver's pci_ski_dev() implementation. This is required to prevent exceptions from the NVIDIA Tegra PCIe controller.
Patch 10 imports the pr_fmt() macro used within the Linux kernel to reduce the number of characters consumed by literal strings by allowing a source file to specify a prefix or suffix that should be applied to all format strings in the file. It is currently used by the debug() and error() macros.
Patches 11-14 are some cleanup and refactoring of I2C core code, the addition of a higher level API that makes it easier for I2C client drivers to talk to devices. The Tegra I2C driver now implements i2c_get_bus_num_fdt() to obtain bus numbers corresponding to a DT node.
Patch 15 implements a driver for the AS3722 PMIC used on the Venice2 and Jetson TK1 boards.
Patches 16-21 are preparatory work for the Tegra PCIe controller. They add missing clock driver functionality as well as drivers for the Tegra powergate and XUSB pad controller blocks.
Patch 22 adds the PCIe controller driver for Tegra20, Tegra30 and Tegra124.
Device tree nodes and configurations options to enable PCIe on the TrimSlice (Tegra20), Beaver, Cardhu (Tegra30) and Jetson TK1 (Tegra124) boards are added in patches 23-31.
Patches 32-35 implement non-cached memory support that will be used in the last batch of patches to implement more reliable packet transfers in the r8169 driver. Patch 36 enables non-cached memory support on Tegra.
Finally, patches 37-40 implement non-cached memory support and various fixes in the r8169 driver and add support for the revision of the NIC found on the Jetson TK1.
The above boards all have an ethernet NIC connected to PCIe, which is what I tested with.
Thierry Reding (40): vsprintf: Add modifier for phys_addr_t fdt: Add a function to count strings fdt: Add a function to get the index of a string fdt: Add functions to retrieve strings fdt: Add resource parsing functions fdt: Add a function to return PCI BDF triplet fdt: Add a subnodes iterator macro pci: Abort early if bus does not exist pci: Honour pci_skip_dev() Add pr_fmt() macro i2c: Initialize the correct bus i2c: Refactor adapter initialization i2c: Add high-level API i2c: tegra: Implement i2c_get_bus_num_fdt() power: Add AMS AS3722 PMIC support ARM: tegra: Implement tegra_plle_enable() ARM: tegra: Provide PCIEXCLK reset ID ARM: tegra: Implement powergate support ARM: tegra: Implement XUSB pad controller ARM: tegra: Add XUSB pad controller on Tegra124 ARM: tegra: Enable XUSB pad controller on Jetson TK1 pci: tegra: Add Tegra PCIe driver ARM: tegra: Add Tegra20 PCIe device tree node ARM: tegra: Enable PCIe on TrimSlice ARM: tegra: Add GIC for Tegra30 ARM: tegra: Add Tegra30 PCIe device tree node ARM: tegra: Enable PCIe on Cardhu ARM: tegra: Enable PCIe on Beaver ARM: tegra: Add GIC for Tegra124 ARM: tegra: Add Tegra124 PCIe device tree node ARM: tegra: Enable PCIe on Jetson TK1 ARM: cache_v7: Various minor cleanups ARM: cache-cp15: Use more accurate types malloc: Output region when debugging ARM: Implement non-cached memory support ARM: tegra: Enable non-cached memory net: rtl8169: Honor CONFIG_SYS_RX_ETH_BUFFER net: rtl8169: Properly align buffers net: rtl8169: Use non-cached memory if available net: rtl8169: Add support for RTL-8168/8111g
README | 19 + arch/arm/cpu/armv7/cache_v7.c | 14 +- arch/arm/cpu/tegra-common/Makefile | 2 + arch/arm/cpu/tegra-common/powergate.c | 102 ++ arch/arm/cpu/tegra-common/xusb-padctl.c | 39 + arch/arm/cpu/tegra124-common/Makefile | 1 + arch/arm/cpu/tegra124-common/clock.c | 109 +++ arch/arm/cpu/tegra124-common/xusb-padctl.c | 716 ++++++++++++++ arch/arm/cpu/tegra20-common/clock.c | 141 ++- arch/arm/cpu/tegra30-common/clock.c | 155 +++ arch/arm/dts/tegra124-jetson-tk1.dts | 373 +++++++ arch/arm/dts/tegra124.dtsi | 90 ++ arch/arm/dts/tegra20-trimslice.dts | 69 ++ arch/arm/dts/tegra20.dtsi | 60 ++ arch/arm/dts/tegra30-beaver.dts | 245 +++++ arch/arm/dts/tegra30-cardhu.dts | 362 +++++++ arch/arm/dts/tegra30.dtsi | 84 ++ arch/arm/include/asm/arch-tegra/powergate.h | 38 + arch/arm/include/asm/arch-tegra/xusb-padctl.h | 24 + arch/arm/include/asm/arch-tegra114/powergate.h | 6 + arch/arm/include/asm/arch-tegra124/clock.h | 2 + arch/arm/include/asm/arch-tegra124/powergate.h | 6 + arch/arm/include/asm/arch-tegra20/clock-tables.h | 2 +- arch/arm/include/asm/arch-tegra20/clock.h | 2 + arch/arm/include/asm/arch-tegra20/powergate.h | 6 + arch/arm/include/asm/arch-tegra30/clock.h | 2 + arch/arm/include/asm/arch-tegra30/powergate.h | 6 + arch/arm/include/asm/system.h | 7 +- arch/arm/lib/cache-cp15.c | 6 +- arch/arm/lib/cache.c | 42 + board/compulab/trimslice/trimslice.c | 8 + board/nvidia/cardhu/cardhu.c | 56 ++ board/nvidia/common/board.c | 3 + board/nvidia/jetson-tk1/jetson-tk1.c | 52 + common/board_r.c | 11 + common/cmd_pci.c | 7 + common/dlmalloc.c | 3 + drivers/i2c/i2c_core.c | 80 +- drivers/i2c/tegra_i2c.c | 13 + drivers/net/rtl8169.c | 127 ++- drivers/pci/Makefile | 1 + drivers/pci/pci.c | 3 + drivers/pci/pci_tegra.c | 1143 ++++++++++++++++++++++ drivers/power/Makefile | 1 + drivers/power/as3722.c | 300 ++++++ include/common.h | 14 +- include/configs/beaver.h | 10 + include/configs/cardhu.h | 10 + include/configs/jetson-tk1.h | 13 + include/configs/tegra-common.h | 1 + include/configs/trimslice.h | 10 + include/dt-bindings/clock/tegra124-car.h | 341 +++++++ include/dt-bindings/clock/tegra20-car.h | 158 +++ include/dt-bindings/clock/tegra30-car.h | 265 +++++ include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 + include/fdtdec.h | 69 ++ include/i2c.h | 96 ++ include/libfdt.h | 72 ++ include/pci.h | 1 + include/power/as3722.h | 27 + lib/fdtdec.c | 76 ++ lib/libfdt/fdt_ro.c | 76 ++ lib/vsprintf.c | 16 +- 63 files changed, 5736 insertions(+), 64 deletions(-) create mode 100644 arch/arm/cpu/tegra-common/powergate.c create mode 100644 arch/arm/cpu/tegra-common/xusb-padctl.c create mode 100644 arch/arm/cpu/tegra124-common/xusb-padctl.c create mode 100644 arch/arm/include/asm/arch-tegra/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra114/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra124/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra20/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra30/powergate.h create mode 100644 drivers/pci/pci_tegra.c create mode 100644 drivers/power/as3722.c create mode 100644 include/dt-bindings/clock/tegra124-car.h create mode 100644 include/dt-bindings/clock/tegra20-car.h create mode 100644 include/dt-bindings/clock/tegra30-car.h create mode 100644 include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h create mode 100644 include/power/as3722.h
Am I right in thinking there will be a V3 series, at least of the part not already awaiting upstream ? If so, maybe it should all be delegated to a single custodian -- right now it is split over five custodians (although part of it -- so that all of it is applied in one go without "foreign" patches getting in between.
I'm waiting to hear from Thierry if he wants me to respin this series since he doesn't have time right now. The DT patches are already in mainline. If it doesn't merge soon it will probably have to move to driver model for I2C...
Regards, Simon
participants (7)
-
Albert ARIBAUD
-
Heiko Schocher
-
Nobuhiro Iwamatsu
-
Simon Glass
-
Stephen Warren
-
Thierry Reding
-
Tom Rini