[U-Boot] [PATCH 00/23] 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).
Patches 1-5 add various FDT helpers to make it easier to parse complex device trees.
Patch 6 is a minor cleanup to the PCI command that prevents a spew of error messages if a bus does not exist. Patch 7 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 8 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 9-14 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 15 adds the PCIe controller driver for Tegra20, Tegra30 and Tegra124.
Finally, patches 16-23 add the various device tree nodes and configuration options to enable PCIe on the TrimSlice (Tegra20), Beaver, Cardhu (Tegra30) and Jetson TK1 (Tegra124) boards.
The above boards all have an ethernet NIC connected to PCIe, which is what I tested with. There are some cache maintenance issues related to that, but a separate series will shortly be sent out to address those.
Thierry
Thierry Reding (23): fdt: Add functions to query a node's #address- and #size-cells fdt: Add a function to get the index of a string 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 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 Tegra30 PCIe device tree node ARM: tegra: Enable PCIe on Beaver ARM: tegra: Enable PCIe on Cardhu ARM: tegra: Add GIC for Tegra124 ARM: tegra: Add Tegra124 PCIe device tree node ARM: tegra: Enable PCIe on Jetson TK1
arch/arm/cpu/tegra-common/Makefile | 2 + arch/arm/cpu/tegra-common/powergate.c | 80 ++ 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 | 766 +++++++++++++++ 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 | 35 + arch/arm/dts/tegra20.dtsi | 56 ++ arch/arm/dts/tegra30-beaver.dts | 166 ++++ arch/arm/dts/tegra30-cardhu.dts | 357 +++++++ arch/arm/dts/tegra30.dtsi | 71 ++ arch/arm/include/asm/arch-tegra/powergate.h | 27 + arch/arm/include/asm/arch-tegra/xusb-padctl.h | 14 + arch/arm/include/asm/arch-tegra114/powergate.h | 6 + arch/arm/include/asm/arch-tegra114/xusb-padctl.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-tegra124/xusb-padctl.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-tegra20/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra30/clock.h | 2 + arch/arm/include/asm/arch-tegra30/powergate.h | 6 + arch/arm/include/asm/arch-tegra30/xusb-padctl.h | 6 + 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 | 218 +++++ common/cmd_pci.c | 7 + drivers/pci/Makefile | 1 + drivers/pci/pci.c | 3 + drivers/pci/pci_tegra.c | 1133 ++++++++++++++++++++++ include/common.h | 14 +- include/configs/beaver.h | 10 + include/configs/cardhu.h | 10 + include/configs/jetson-tk1.h | 10 + 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 | 64 ++ include/libfdt.h | 59 ++ include/pci.h | 1 + lib/fdtdec.c | 61 ++ lib/libfdt/fdt_ro.c | 64 ++ 51 files changed, 5033 insertions(+), 8 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-tegra114/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra124/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra124/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra20/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra20/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra30/powergate.h create mode 100644 arch/arm/include/asm/arch-tegra30/xusb-padctl.h create mode 100644 drivers/pci/pci_tegra.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

From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com --- include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/** + * fdt_n_addr_cells() - find the number of address cells required by a node + * + * Looks up the #address-cells property of the node to examine. If that has + * no such property, walks up the device tree until it finds one in one of + * the device's parents. If no #address-cells property is found, it is + * assumed to be 1. + * + * @param fdt FDT blob + * @param node node to examine + * @return number of address cells + */ +int fdt_n_addr_cells(const void *fdt, int node); + +/** + * fdt_n_size_cells() - find the number of size cells required by a node + * + * Looks up the #size-cells property of the node to examine. If that has no + * such property, walks up the device tree until it finds one in one of the + * device's parents. If no #size-cells property is found, it is assumed to + * be 1. + * + * @param fdt FDT blob + * @param node node to examine + * @return number of size cells + */ +int fdt_n_size_cells(const void *fdt, int node); + #endif /* _LIBFDT_H */ diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c index 36af0435254b..17cd11333c1e 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -530,3 +530,39 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
return offset; /* error from fdt_next_node() */ } + +int fdt_n_addr_cells(const void *fdt, int node) +{ + const fdt32_t *prop; + int len, parent; + + do { + parent = fdt_parent_offset(fdt, node); + if (parent >= 0) + node = parent; + + prop = fdt_getprop(fdt, node, "#address-cells", &len); + if (prop) + return fdt32_to_cpu(*prop); + } while (parent >= 0); + + return 1; +} + +int fdt_n_size_cells(const void *fdt, int node) +{ + const fdt32_t *prop; + int len, parent; + + do { + parent = fdt_parent_offset(fdt, node); + if (parent >= 0) + node = parent; + + prop = fdt_getprop(fdt, node, "#size-cells", &len); + if (prop) + return fdt32_to_cpu(*prop); + } while (parent >= 0); + + return 1; +}

Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by a node
- Looks up the #address-cells property of the node to examine. If that has
- no such property, walks up the device tree until it finds one in one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a node
Also fdt_size_cells().
Regards, Simon

On Mon, Aug 18, 2014 at 11:52:36AM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by a node
- Looks up the #address-cells property of the node to examine. If that has
- no such property, walks up the device tree until it finds one in one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a node
Also fdt_size_cells().
Neither of those seem to walk up the tree, so inheriting #address-cells or #size-cells from a parent will not work.
According to the comments in the code they're also supposed to return the number of cells represented by a bus, not a device (which might explain why it doesn't walk up the tree).
I also can't reuse them to implement these fdt_n_addr_cells() and fdt_n_size_cells() functions because they don't return an accurate error if the #address-cells and #size-cells are not found, therefore it's impossible to decide when to climb further up the tree.
Thierry

Hi Theirry,
On 19 August 2014 04:59, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 11:52:36AM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com
wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char *
const inc[], int inc_count,
struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by
a node
- Looks up the #address-cells property of the node to examine. If
that has
- no such property, walks up the device tree until it finds one in
one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a
node
Also fdt_size_cells().
Neither of those seem to walk up the tree, so inheriting #address-cells or #size-cells from a parent will not work.
According to the comments in the code they're also supposed to return the number of cells represented by a bus, not a device (which might explain why it doesn't walk up the tree).
I also can't reuse them to implement these fdt_n_addr_cells() and fdt_n_size_cells() functions because they don't return an accurate error if the #address-cells and #size-cells are not found, therefore it's impossible to decide when to climb further up the tree.
OK but I have one more question. I thought that dtc gave a warning when you don't explicitly specify these values in the parent node?
Regards, Simon

On Tue, Aug 19, 2014 at 06:52:22AM -0600, Simon Glass wrote:
Hi Theirry,
On 19 August 2014 04:59, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 11:52:36AM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com
wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char *
const inc[], int inc_count,
struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by
a node
- Looks up the #address-cells property of the node to examine. If
that has
- no such property, walks up the device tree until it finds one in
one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a
node
Also fdt_size_cells().
Neither of those seem to walk up the tree, so inheriting #address-cells or #size-cells from a parent will not work.
According to the comments in the code they're also supposed to return the number of cells represented by a bus, not a device (which might explain why it doesn't walk up the tree).
I also can't reuse them to implement these fdt_n_addr_cells() and fdt_n_size_cells() functions because they don't return an accurate error if the #address-cells and #size-cells are not found, therefore it's impossible to decide when to climb further up the tree.
OK but I have one more question. I thought that dtc gave a warning when you don't explicitly specify these values in the parent node?
It doesn't explicitly warn about the properties being absent. Rather it will fallback to the default (#address-cells = <2>, #size-cells = <1> in the version that I have) and warn if those don't match what's expected.
Interestingly as opposed to what the Linux kernel does, DTC doesn't seem to climb further up the tree if it can't find the properties in a device tree node. Instead, it seems to always use the default values instead.
Adding the devicetree@vger.kernel.org mailing list. Maybe somebody there can help iron out this inconsistency. ePAPR explicitly says that both "... #address-cells and #size-cells properties are not inherited from ancestors in the device tree. They shall be explicitly defined." (see 2.3.5 "#address-cells and #size-cells").
Thierry

Hi Thierry,
On 19 August 2014 07:06, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 06:52:22AM -0600, Simon Glass wrote:
Hi Theirry,
On 19 August 2014 04:59, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 11:52:36AM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com
wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char *
const inc[], int inc_count,
struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by
a node
- Looks up the #address-cells property of the node to examine. If
that has
- no such property, walks up the device tree until it finds one in
one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a
node
Also fdt_size_cells().
Neither of those seem to walk up the tree, so inheriting #address-cells or #size-cells from a parent will not work.
According to the comments in the code they're also supposed to return the number of cells represented by a bus, not a device (which might explain why it doesn't walk up the tree).
I also can't reuse them to implement these fdt_n_addr_cells() and fdt_n_size_cells() functions because they don't return an accurate error if the #address-cells and #size-cells are not found, therefore it's impossible to decide when to climb further up the tree.
OK but I have one more question. I thought that dtc gave a warning when you don't explicitly specify these values in the parent node?
It doesn't explicitly warn about the properties being absent. Rather it will fallback to the default (#address-cells = <2>, #size-cells = <1> in the version that I have) and warn if those don't match what's expected.
Interestingly as opposed to what the Linux kernel does, DTC doesn't seem to climb further up the tree if it can't find the properties in a device tree node. Instead, it seems to always use the default values instead.
Adding the devicetree@vger.kernel.org mailing list. Maybe somebody there can help iron out this inconsistency. ePAPR explicitly says that both "... #address-cells and #size-cells properties are not inherited from ancestors in the device tree. They shall be explicitly defined." (see 2.3.5 "#address-cells and #size-cells").
That's my understanding too. So should we drop this patch?
Regards, Simon

On Fri, Aug 22, 2014 at 09:03:49PM -0600, Simon Glass wrote:
Hi Thierry,
On 19 August 2014 07:06, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 06:52:22AM -0600, Simon Glass wrote:
Hi Theirry,
On 19 August 2014 04:59, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 11:52:36AM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com
wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node, the fdt_n_addr_cells() function will walk up the device tree and search for an #address-cells property. It returns the number of cells required by the device tree node to represent an address.
Similarly the fdt_n_size_cells() function returns the number of cells required by the given device tree node to represent a size. It achieves that by walking up the device tree in seach for a #size-cells property.
If no #address-cells or #size-cells property can be found, both of the functions return 1.
Signed-off-by: Thierry Reding treding@nvidia.com
include/libfdt.h | 28 ++++++++++++++++++++++++++++ lib/libfdt/fdt_ro.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index a1ef1e15df3d..e7f991b388cf 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1638,4 +1638,32 @@ int fdt_find_regions(const void *fdt, char *
const inc[], int inc_count,
struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/**
- fdt_n_addr_cells() - find the number of address cells required by
a node
- Looks up the #address-cells property of the node to examine. If
that has
- no such property, walks up the device tree until it finds one in
one of
- the device's parents. If no #address-cells property is found, it is
- assumed to be 1.
- @param fdt FDT blob
- @param node node to examine
- @return number of address cells
- */
+int fdt_n_addr_cells(const void *fdt, int node);
There is a new fdt_address_cells() recently that looks suitable.
+/**
- fdt_n_size_cells() - find the number of size cells required by a
node
Also fdt_size_cells().
Neither of those seem to walk up the tree, so inheriting #address-cells or #size-cells from a parent will not work.
According to the comments in the code they're also supposed to return the number of cells represented by a bus, not a device (which might explain why it doesn't walk up the tree).
I also can't reuse them to implement these fdt_n_addr_cells() and fdt_n_size_cells() functions because they don't return an accurate error if the #address-cells and #size-cells are not found, therefore it's impossible to decide when to climb further up the tree.
OK but I have one more question. I thought that dtc gave a warning when you don't explicitly specify these values in the parent node?
It doesn't explicitly warn about the properties being absent. Rather it will fallback to the default (#address-cells = <2>, #size-cells = <1> in the version that I have) and warn if those don't match what's expected.
Interestingly as opposed to what the Linux kernel does, DTC doesn't seem to climb further up the tree if it can't find the properties in a device tree node. Instead, it seems to always use the default values instead.
Adding the devicetree@vger.kernel.org mailing list. Maybe somebody there can help iron out this inconsistency. ePAPR explicitly says that both "... #address-cells and #size-cells properties are not inherited from ancestors in the device tree. They shall be explicitly defined." (see 2.3.5 "#address-cells and #size-cells").
That's my understanding too. So should we drop this patch?
Yes, in this particular case it works fine using the existing fdt_address_cells() and fdt_size_cells() functions so I've dropped this patch.
Thierry

From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the fdt_get_string_index() 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 --- include/libfdt.h | 11 +++++++++++ lib/libfdt/fdt_ro.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index e7f991b388cf..4d7fb2681669 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -857,6 +857,17 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+/** + * fdt_get_string_index - get the index of a string 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 + * @string: string to look up in the string list + * @return the index of the string or negative on error + */ +int fdt_get_string_index(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 17cd11333c1e..f211c64ca7ca 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -14,6 +14,8 @@
#include "libfdt_internal.h"
+#define max(x, y) (((x) < (y)) ? (y) : (x)) + static int _fdt_nodename_eq(const void *fdt, int offset, const char *s, int len) { @@ -491,6 +493,32 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) return 0; }
+int fdt_get_string_index(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; + + while (list < end) { + int n = strlen(string); + int m = strlen(list); + + if (n == m && memcmp(list, string, n) == 0) + return index; + + list += max(n, m) + 1; + index++; + } + + return -FDT_ERR_NOTFOUND; +} + int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) {

Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
Given a device tree node and a property name, the fdt_get_string_index() 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
include/libfdt.h | 11 +++++++++++ lib/libfdt/fdt_ro.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index e7f991b388cf..4d7fb2681669 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -857,6 +857,17 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+/**
- fdt_get_string_index - get the index of a string 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
- @string: string to look up in the string list
- @return the index of the string or negative on error
- */
+int fdt_get_string_index(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 17cd11333c1e..f211c64ca7ca 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -14,6 +14,8 @@
#include "libfdt_internal.h"
+#define max(x, y) (((x) < (y)) ? (y) : (x))
static int _fdt_nodename_eq(const void *fdt, int offset, const char *s, int len) { @@ -491,6 +493,32 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) return 0; }
+int fdt_get_string_index(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;
while (list < end) {
int n = strlen(string);
This can go outside the loop.
int m = strlen(list);
if (n == m && memcmp(list, string, n) == 0)
return index;
list += max(n, m) + 1;
I worry that if n > m this will end up in the middle of a the next string in the list. What is the intention here?
index++;
}
return -FDT_ERR_NOTFOUND;
+}
int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) { -- 2.0.4
Regards, Simon

On Mon, Aug 18, 2014 at 11:58:09AM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
[...]
+int fdt_get_string_index(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;
while (list < end) {
int n = strlen(string);
This can go outside the loop.
Indeed.
int m = strlen(list);
if (n == m && memcmp(list, string, n) == 0)
return index;
list += max(n, m) + 1;
I worry that if n > m this will end up in the middle of a the next string in the list. What is the intention here?
I can no longer recall why that's there. It certainly seems wrong since if the current substring isn't what we're looking for the function should proceed to the next one, which is always m + 1 characters away.
I'll replace this with list += m + 1.
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 --- include/fdtdec.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/fdtdec.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 856e6cf766de..e9091eee6bae 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -40,6 +40,23 @@ struct fdt_memory { fdt_addr_t end; };
+/* information about a resource */ +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 +600,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 names name of the property to obtain the match the name to + * @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 *names, const char *name, + struct fdt_resource *res); + #endif diff --git a/lib/fdtdec.c b/lib/fdtdec.c index eb5aa20526fd..fbfae4a7cbaf 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -691,4 +691,47 @@ int fdtdec_read_fmap_entry(const void *blob, int node, const char *name,
return 0; } + +int fdt_get_resource(const void *fdt, int node, const char *property, + unsigned int index, struct fdt_resource *res) +{ + const fdt32_t *ptr, *end; + unsigned int i = 0; + int na, ns, len; + + na = fdt_n_addr_cells(fdt, node); + ns = fdt_n_size_cells(fdt, node); + + ptr = fdt_getprop(fdt, node, property, &len); + if (!ptr) + return len; + + end = ptr + len / 4; + + while (ptr + na + ns <= end) { + if (i == index) { + res->start = fdt_addr_to_cpu(*ptr); + res->end = res->start + fdt_size_to_cpu(ptr[na]) - 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 *names, const char *name, + struct fdt_resource *res) +{ + int index; + + index = fdt_get_string_index(fdt, node, names, name); + if (index < 0) + return index; + + return fdt_get_resource(fdt, node, property, index, res); +} #endif

Hi Thierry,
On 18 August 2014 01:16, 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
include/fdtdec.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/fdtdec.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 856e6cf766de..e9091eee6bae 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -40,6 +40,23 @@ struct fdt_memory { fdt_addr_t end; };
+/* information about a resource */
Please add comments, e.g. that end is inclusive.
+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 +600,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 names name of the property to obtain the match the name to
- @param name the name of the entry to look up
These two parameters are confusing. Perhaps rename names to something better?
- @param res returns the resource
- */
+int fdt_get_named_resource(const void *fdt, int node, const char *property,
const char *names, const char *name,
struct fdt_resource *res);
#endif diff --git a/lib/fdtdec.c b/lib/fdtdec.c index eb5aa20526fd..fbfae4a7cbaf 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -691,4 +691,47 @@ int fdtdec_read_fmap_entry(const void *blob, int node, const char *name,
return 0;
}
+int fdt_get_resource(const void *fdt, int node, const char *property,
unsigned int index, struct fdt_resource *res)
s/index/find_index/
+{
const fdt32_t *ptr, *end;
unsigned int i = 0;
int na, ns, len;
na = fdt_n_addr_cells(fdt, node);
ns = fdt_n_size_cells(fdt, node);
ptr = fdt_getprop(fdt, node, property, &len);
if (!ptr)
return len;
end = ptr + len / 4;
sizeof(*ptr) might be better than 4.
while (ptr + na + ns <= end) {
if (i == index) {
res->start = fdt_addr_to_cpu(*ptr);
This doesn't deal with 64-bit addresses. There is a half-hearted attempt with fdt_addr_t, but I wonder if we need a helper function like fdt_get_addr(ptr, num_cells)?
res->end = res->start + fdt_size_to_cpu(ptr[na]) - 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 *names, const char *name,
struct fdt_resource *res)
+{
int index;
index = fdt_get_string_index(fdt, node, names, name);
if (index < 0)
return index;
return fdt_get_resource(fdt, node, property, index, res);
+}
#endif
2.0.4
Regards, Simon

On Mon, Aug 18, 2014 at 12:06:26PM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, 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
include/fdtdec.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/fdtdec.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 856e6cf766de..e9091eee6bae 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -40,6 +40,23 @@ struct fdt_memory { fdt_addr_t end; };
+/* information about a resource */
Please add comments, e.g. that end is inclusive.
I've added this comment:
/* * 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. */
Is that enough or do you want me to be more verbose?
+/**
- 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 names name of the property to obtain the match the name to
- @param name the name of the entry to look up
These two parameters are confusing. Perhaps rename names to something better?
How about "prop_names" so that it indicates it is the name of a property? I've also adjusted the comment a bit:
* @param prop_names name of the property containing the list of names
Hopefully that will make it more obvious.
+int fdt_get_resource(const void *fdt, int node, const char *property,
unsigned int index, struct fdt_resource *res)
s/index/find_index/
In my opinion the find_ prefix is redundant. After all the function name already indicates that we're looking for a resource inside a property. And index would be the offset in that property.
if (!ptr)
return len;
end = ptr + len / 4;
sizeof(*ptr) might be better than 4.
Device tree explicitly specifies that cells are 32-bit, so this can't ever be anything other than 4. But I'll change it anyway if you prefer.
while (ptr + na + ns <= end) {
if (i == index) {
res->start = fdt_addr_to_cpu(*ptr);
This doesn't deal with 64-bit addresses. There is a half-hearted attempt with fdt_addr_t, but I wonder if we need a helper function like fdt_get_addr(ptr, num_cells)?
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0;
while (size--) r = (r << 32) | be32_to_cpu(*(cell++));
return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
Thierry

Hi Thierry,
On 19 August 2014 05:35, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 12:06:26PM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, 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
include/fdtdec.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/fdtdec.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 856e6cf766de..e9091eee6bae 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -40,6 +40,23 @@ struct fdt_memory { fdt_addr_t end; };
+/* information about a resource */
Please add comments, e.g. that end is inclusive.
I've added this comment:
/*
- 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.
*/
Is that enough or do you want me to be more verbose?
LGTM
+/**
- 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 names name of the property to obtain the match the name to
- @param name the name of the entry to look up
These two parameters are confusing. Perhaps rename names to something better?
How about "prop_names" so that it indicates it is the name of a property? I've also adjusted the comment a bit:
- @param prop_names name of the property containing the list of names
Hopefully that will make it more obvious.
OK
+int fdt_get_resource(const void *fdt, int node, const char *property,
unsigned int index, struct fdt_resource *res)
s/index/find_index/
In my opinion the find_ prefix is redundant. After all the function name already indicates that we're looking for a resource inside a property. And index would be the offset in that property.
OK
if (!ptr)
return len;
end = ptr + len / 4;
sizeof(*ptr) might be better than 4.
Device tree explicitly specifies that cells are 32-bit, so this can't ever be anything other than 4. But I'll change it anyway if you prefer.
I see sizeof() used in the lifdt code, so this might not go upstream as is.
while (ptr + na + ns <= end) {
if (i == index) {
res->start = fdt_addr_to_cpu(*ptr);
This doesn't deal with 64-bit addresses. There is a half-hearted attempt with fdt_addr_t, but I wonder if we need a helper function like fdt_get_addr(ptr, num_cells)?
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; while (size--) r = (r << 32) | be32_to_cpu(*(cell++)); return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
That looks good. It works for the cases we need and it's obvious later where the logic is if we want to extend it.
Regards, Simon

On Tue, Aug 19, 2014 at 06:55:18AM -0600, Simon Glass wrote:
On 19 August 2014 05:35, Thierry Reding thierry.reding@gmail.com wrote:
[...]
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; while (size--) r = (r << 32) | be32_to_cpu(*(cell++)); return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
That looks good. It works for the cases we need and it's obvious later where the logic is if we want to extend it.
This is what I have for U-Boot currently.
static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { fdt_addr_t addr = 0;
while (cells--) /* do the shift in two steps to avoid warning on 32-bit */ addr = (addr << 16) << 16 | fdt32_to_cpu(*ptr++);
return addr; }
I use the same function to parse the size field of reg entries, even though the types aren't technically the same. But making the types consistent would duplicate the above exactly, so I'm not sure it's worth it.
Alternatively perhaps something like this could be done:
static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) { /* the above */ }
static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); }
static fdt_size_t fdtdec_get_size(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); }
Again, I'm not sure it's really worth the trouble since fdt_addr_t and fdt_size_t are both always either u32 or u64.
Thierry

Hi Thierry,
On 19 August 2014 07:12, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 06:55:18AM -0600, Simon Glass wrote:
On 19 August 2014 05:35, Thierry Reding thierry.reding@gmail.com wrote:
[...]
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; while (size--) r = (r << 32) | be32_to_cpu(*(cell++)); return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
That looks good. It works for the cases we need and it's obvious later where the logic is if we want to extend it.
This is what I have for U-Boot currently.
static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { fdt_addr_t addr = 0;
while (cells--) /* do the shift in two steps to avoid warning on 32-bit */ addr = (addr << 16) << 16 | fdt32_to_cpu(*ptr++);
Odd warning! Does 32UL help?
return addr;
}
I use the same function to parse the size field of reg entries, even though the types aren't technically the same. But making the types consistent would duplicate the above exactly, so I'm not sure it's worth it.
Seems fine.
Alternatively perhaps something like this could be done:
static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) { /* the above */ } static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); } static fdt_size_t fdtdec_get_size(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); }
Again, I'm not sure it's really worth the trouble since fdt_addr_t and fdt_size_t are both always either u32 or u64.
Yes, although I suppose ultimately these might be 64-bit always, Perhaps we should merge the types?
Regards, Simon

On Tue, Aug 19, 2014 at 03:28:09PM -0600, Simon Glass wrote:
Hi Thierry,
On 19 August 2014 07:12, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 06:55:18AM -0600, Simon Glass wrote:
On 19 August 2014 05:35, Thierry Reding thierry.reding@gmail.com wrote:
[...]
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; while (size--) r = (r << 32) | be32_to_cpu(*(cell++)); return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
That looks good. It works for the cases we need and it's obvious later where the logic is if we want to extend it.
This is what I have for U-Boot currently.
static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { fdt_addr_t addr = 0;
while (cells--) /* do the shift in two steps to avoid warning on 32-bit */ addr = (addr << 16) << 16 | fdt32_to_cpu(*ptr++);
Odd warning! Does 32UL help?
The exact warning that I get is this:
warning: left shift count >= width of type
So the problem is that since addr is of type fdt_addr_t which is 32-bit, we're effectively shifting all bits out of the variable. The compiler will generate the same assembly code whether or not I use the single 32- bit shift or two 16-bit shifts, but using the latter the warning is gone. That's on both 32-bit and 64-bit ARM.
Alternatively perhaps something like this could be done:
static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) { /* the above */ } static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); } static fdt_size_t fdtdec_get_size(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); }
Again, I'm not sure it's really worth the trouble since fdt_addr_t and fdt_size_t are both always either u32 or u64.
Yes, although I suppose ultimately these might be 64-bit always, Perhaps we should merge the types?
That's one other possibility. On Linux there's one common type for these:
typedef phys_addr_t resource_size_t;
where phys_addr_t is defined as follows:
#ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif
Perhaps we should simply copy that. I take it CONFIG_PHYS_64BIT is U-Boot's equivalent of CONFIG_PHYS_ADDR_T_64BIT? It doesn't seem to be documented anywhere but its usage would indicate that it is. I don't think U-Boot supports things like LPAE, so there's probably no need to control this more fine-grainedly than with a single CONFIG_PHYS_64BIT setting.
Thierry

Hi Thierry,
On 20 August 2014 00:36, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 03:28:09PM -0600, Simon Glass wrote:
Hi Thierry,
On 19 August 2014 07:12, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Aug 19, 2014 at 06:55:18AM -0600, Simon Glass wrote:
On 19 August 2014 05:35, Thierry Reding thierry.reding@gmail.com wrote:
[...]
The Linux kernel handles this by wrapping it in an of_read_number() helper and always returning u64, like this:
static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; while (size--) r = (r << 32) | be32_to_cpu(*(cell++)); return r; }
It obviously only works for size in { 0, 1, 2 }, but I can add a helper like that if you want.
That looks good. It works for the cases we need and it's obvious later where the logic is if we want to extend it.
This is what I have for U-Boot currently.
static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { fdt_addr_t addr = 0;
while (cells--) /* do the shift in two steps to avoid warning on 32-bit */ addr = (addr << 16) << 16 | fdt32_to_cpu(*ptr++);
Odd warning! Does 32UL help?
The exact warning that I get is this:
warning: left shift count >= width of type
So the problem is that since addr is of type fdt_addr_t which is 32-bit, we're effectively shifting all bits out of the variable. The compiler will generate the same assembly code whether or not I use the single 32- bit shift or two 16-bit shifts, but using the latter the warning is gone. That's on both 32-bit and 64-bit ARM.
Well if fdt_addr_t is 32-bit then you are don't going to get a better result with your change - the warning is correct. So I think you need to have an #ifdef for the case where CONFIG_PHYS_64BIT is not defined and deal with it separate. Yes would could just move to 64-bit everwhere, but that will bloat the code I suspect (as we can always do it later anyway).
Alternatively perhaps something like this could be done:
static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) { /* the above */ } static fdt_addr_t fdtdec_get_address(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); } static fdt_size_t fdtdec_get_size(const fdt32_t *ptr, unsigned int cells) { return fdtdec_get_number(ptr, cells); }
Again, I'm not sure it's really worth the trouble since fdt_addr_t and fdt_size_t are both always either u32 or u64.
Yes, although I suppose ultimately these might be 64-bit always, Perhaps we should merge the types?
That's one other possibility. On Linux there's one common type for these:
typedef phys_addr_t resource_size_t;
where phys_addr_t is defined as follows:
#ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif
Perhaps we should simply copy that. I take it CONFIG_PHYS_64BIT is U-Boot's equivalent of CONFIG_PHYS_ADDR_T_64BIT? It doesn't seem to be documented anywhere but its usage would indicate that it is. I don't think U-Boot supports things like LPAE, so there's probably no need to control this more fine-grainedly than with a single CONFIG_PHYS_64BIT setting.
U-Boot does have LPAE on x86 at least. I think using resource_size_t is a good approach.
Regards, Simon

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.
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 e9091eee6bae..56bc29375128 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -631,4 +631,15 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property, const char *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 fbfae4a7cbaf..d2f2c9164b37 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -734,4 +734,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

On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org

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.
Signed-off-by: Thierry Reding treding@nvidia.com --- include/libfdt.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index 4d7fb2681669..0330d7a29a58 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -163,6 +163,26 @@ 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 + * ... + * } + * + * @fdt: FDT blob + * @node: child node + * @parent: parent node + */ +#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 */ /**********************************************************************/

On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Will this go upstream to dtc at some point?
include/libfdt.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index 4d7fb2681669..0330d7a29a58 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -163,6 +163,26 @@ 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
...
}
- @fdt: FDT blob
- @node: child node
- @parent: parent node
It might be worth mentioning the type of each of these since it is not in the macro.
- */
+#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 */ /**********************************************************************/ -- 2.0.4
Regards, Simon

On Mon, Aug 18, 2014 at 12:11:03PM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Will this go upstream to dtc at some point?
I suppose it could. I don't know anything about how code flows back and forth between dtc and U-Boot.
include/libfdt.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index 4d7fb2681669..0330d7a29a58 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -163,6 +163,26 @@ 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
...
}
- @fdt: FDT blob
- @node: child node
- @parent: parent node
It might be worth mentioning the type of each of these since it is not in the macro.
Will do.
Thierry

Hi Thierry,
On 19 August 2014 06:22, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 12:11:03PM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Will this go upstream to dtc at some point?
I suppose it could. I don't know anything about how code flows back and forth between dtc and U-Boot.
Well normally we try to send changes upstream to minimise the diff between libfdt and U-Boot. You can join devicetree-compiler@vger.kernel.org and send patches. If they get modified upstream then I can apply a diff to U-Boot when they are applied.
Regards, Simon

On Tue, Aug 19, 2014 at 06:57:54AM -0600, Simon Glass wrote:
Hi Thierry,
On 19 August 2014 06:22, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 12:11:03PM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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.
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Will this go upstream to dtc at some point?
I suppose it could. I don't know anything about how code flows back and forth between dtc and U-Boot.
Well normally we try to send changes upstream to minimise the diff between libfdt and U-Boot. You can join devicetree-compiler@vger.kernel.org and send patches. If they get modified upstream then I can apply a diff to U-Boot when they are applied.
Okay, I'll prepare a patch against upstream dtc then.
Thierry

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

On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Seems OK. But I wonder if a string might be simpler?
#define DEBUG_PREFIX "foo: "
+#ifndef DEBUG_PREFIX +#define DEBUG_PREFIX "" +#endif
... printf(DEBUG_PREFIX ##args); \
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)
-- 2.0.4
Regards, Simon

On Mon, Aug 18, 2014 at 12:24:06PM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Seems OK. But I wonder if a string might be simpler?
#define DEBUG_PREFIX "foo: "
+#ifndef DEBUG_PREFIX +#define DEBUG_PREFIX "" +#endif
The advantage of using the macro is that it can perform more complicated transformations on the format string, like this for example:
#define pr_fmt(fmt) "foo: %s():" fmt, __func__
Thierry

Hi Thierry,
On 19 August 2014 06:27, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 12:24:06PM -0600, Simon Glass wrote:
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Signed-off-by: Thierry Reding treding@nvidia.com
Acked-by: Simon Glass sjg@chromium.org
Seems OK. But I wonder if a string might be simpler?
#define DEBUG_PREFIX "foo: "
+#ifndef DEBUG_PREFIX +#define DEBUG_PREFIX "" +#endif
The advantage of using the macro is that it can perform more complicated transformations on the format string, like this for example:
#define pr_fmt(fmt) "foo: %s():" fmt, __func__
OK, I figured you might want something like that.
Thierry
Regards, Simon

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.
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_ */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
I see no issue with the structure of the code so, Acked-by: Stephen Warren swarren@nvidia.com
I know too little about the CAR/PLLE to comment on the actual register IO performed here; someone like Peter De Schrijver should review this really...

From: Thierry Reding treding@nvidia.com
This reset is required for PCIe and the corresponding ID therefore needs to be defined.
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,

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
This reset is required for PCIe and the corresponding ID therefore needs to be defined.
It might be worth mentioning in the commit description why this patch does different things for each SoC; namely that some SoCs already define this ID, and the code already contains the parts you're adding here.
Aside from that, Acked-by: Stephen Warren swarren@nvidia.com

On Wed, Aug 20, 2014 at 12:20:20PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
This reset is required for PCIe and the corresponding ID therefore needs to be defined.
It might be worth mentioning in the commit description why this patch does different things for each SoC; namely that some SoCs already define this ID, and the code already contains the parts you're adding here.
Done.
Aside from that, Acked-by: Stephen Warren swarren@nvidia.com
Thanks, Thierry

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 --- arch/arm/cpu/tegra-common/Makefile | 1 + arch/arm/cpu/tegra-common/powergate.c | 80 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-tegra/powergate.h | 27 +++++++++ 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, 132 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..2097bbcde365 --- /dev/null +++ b/arch/arm/cpu/tegra-common/powergate.c @@ -0,0 +1,80 @@ +#include <common.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 PWRGATE_STATUS 0x38 + +static int tegra_powergate_set(enum tegra_powergate id, bool state) +{ + unsigned long value; + bool old_state; + + value = readl(NV_PA_PMC_BASE + PWRGATE_STATUS); + old_state = value & (1 << id); + + if (state == old_state) + return 0; + + writel(PWRGATE_TOGGLE_START | id, NV_PA_PMC_BASE + PWRGATE_TOGGLE); + + return 0; +} + +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; + + 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 + 0x34); + + 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..53bed37d42c1 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/powergate.h @@ -0,0 +1,27 @@ +#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, +}; + +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_ */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Implement the powergate API that allows various power partitions to be power up and down.
diff --git a/arch/arm/cpu/tegra-common/powergate.c b/arch/arm/cpu/tegra-common/powergate.c
+static int tegra_powergate_set(enum tegra_powergate id, bool state)
- writel(PWRGATE_TOGGLE_START | id, NV_PA_PMC_BASE + PWRGATE_TOGGLE);
Since the power-down/up is an asynchronous operation, don't you need to wait for it to complete here?
- return 0;
+}
+static int tegra_powergate_remove_clamping(enum tegra_powergate id) +{
- unsigned long value;
- 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;
A comment indicating why there's a special case here would be useful.
Isn't the special-case (HW design bug) restricted to Tegra20, or did it carry over into later chips in order to maintain HW register compatibility?
diff --git a/arch/arm/include/asm/arch-tegra/powergate.h b/arch/arm/include/asm/arch-tegra/powergate.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,
+};
I thought the list of partitions varied a bit by chip?
Overall the code structure looks good, and on the condition I'm wrong about the variations between chips, Acked-by: Stephen Warren swarren@nvidia.com

On Wed, Aug 20, 2014 at 12:24:11PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Implement the powergate API that allows various power partitions to be power up and down.
diff --git a/arch/arm/cpu/tegra-common/powergate.c b/arch/arm/cpu/tegra-common/powergate.c
+static int tegra_powergate_set(enum tegra_powergate id, bool state)
- writel(PWRGATE_TOGGLE_START | id, NV_PA_PMC_BASE + PWRGATE_TOGGLE);
Since the power-down/up is an asynchronous operation, don't you need to wait for it to complete here?
It seems like the PWRGATE_STATUS register can be polled to determine when the operation is finished. I'll implement a loop with a small timeout here. We probably need to do the same in the kernel.
Apparently for newer SoCs (starting with Tegra114) the meaning of the PWRGATE_TOGGLE_START bit has also changed. It should be polled until cleared before starting a new operation and once an operation has been started (PWRGATE_TOGGLE_START set) the bit should be polled until it is cleared again to check that the request was accepted by the PMC.
- return 0;
+}
+static int tegra_powergate_remove_clamping(enum tegra_powergate id) +{
- unsigned long value;
- 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;
A comment indicating why there's a special case here would be useful.
Isn't the special-case (HW design bug) restricted to Tegra20, or did it carry over into later chips in order to maintain HW register compatibility?
As far as I can tell this was carried over for register compatibility. The bits for the PCIE and VDEC partitions are still reversed in Tegra124.
diff --git a/arch/arm/include/asm/arch-tegra/powergate.h b/arch/arm/include/asm/arch-tegra/powergate.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,
+};
I thought the list of partitions varied a bit by chip?
The list of valid partitions varies per chip, but the list of values remains compatible. For the same reason we have a list of partitions in the Linux kernel, but they are checked for validity against the per-SoC list. Not sure if we need to go that far with U-Boot.
Thierry

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 --- 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 | 766 +++++++++++++++++++++++ arch/arm/include/asm/arch-tegra/xusb-padctl.h | 14 + arch/arm/include/asm/arch-tegra114/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra124/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra20/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra30/xusb-padctl.h | 6 + board/nvidia/common/board.c | 3 + include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 + include/fdtdec.h | 2 + lib/fdtdec.c | 1 + 13 files changed, 858 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 arch/arm/include/asm/arch-tegra114/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra124/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra20/xusb-padctl.h create mode 100644 arch/arm/include/asm/arch-tegra30/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..2a1d860844c7 --- /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/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..bceacff59fb7 --- /dev/null +++ b/arch/arm/cpu/tegra124-common/xusb-padctl.c @@ -0,0 +1,766 @@ +/* + * 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/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, + }, + }, +}; + +int fdtdec_count_strings(const void *fdt, int node, const char *prop_name) +{ + int length, i, count = 0; + const char *list; + + list = fdt_getprop(fdt, node, prop_name, &length); + if (!list) + return -length; + + for (i = 0; i < length; i++) { + int len = strlen(list); + + list += len + 1; + i += len; + count++; + } + + return count; +} + +int fdtdec_get_string_index(const void *fdt, int node, const char *prop_name, + int index, const char **output) +{ + const char *list; + int length, i; + + list = fdt_getprop(fdt, node, prop_name, &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 fdtdec_get_string(const void *fdt, int node, const char *prop_name, + const char **output) +{ + return fdtdec_get_string_index(fdt, node, prop_name, 0, output); +} + +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 = fdtdec_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 = fdtdec_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 = fdtdec_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..39d01ae0132e --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/xusb-padctl.h @@ -0,0 +1,14 @@ +#ifndef _TEGRA_XUSB_PADCTL_H_ +#define _TEGRA_XUSB_PADCTL_H_ + +struct tegra_xusb_phy; + +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/arch/arm/include/asm/arch-tegra114/xusb-padctl.h b/arch/arm/include/asm/arch-tegra114/xusb-padctl.h new file mode 100644 index 000000000000..2b5c5ff4c912 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra114/xusb-padctl.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA114_XUSB_PADCTL_H_ +#define _TEGRA114_XUSB_PADCTL_H_ + +#include <asm/arch-tegra/xusb-padctl.h> + +#endif /* _TEGRA114_XUSB_PADCTL_H_ */ diff --git a/arch/arm/include/asm/arch-tegra124/xusb-padctl.h b/arch/arm/include/asm/arch-tegra124/xusb-padctl.h new file mode 100644 index 000000000000..90a9d8042391 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra124/xusb-padctl.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA124_XUSB_PADCTL_H_ +#define _TEGRA124_XUSB_PADCTL_H_ + +#include <asm/arch-tegra/xusb-padctl.h> + +#endif /* _TEGRA124_XUSB_PADCTL_H_ */ diff --git a/arch/arm/include/asm/arch-tegra20/xusb-padctl.h b/arch/arm/include/asm/arch-tegra20/xusb-padctl.h new file mode 100644 index 000000000000..8204dcdc696b --- /dev/null +++ b/arch/arm/include/asm/arch-tegra20/xusb-padctl.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA20_XUSB_PADCTL_H_ +#define _TEGRA20_XUSB_PADCTL_H_ + +#include <asm/arch-tegra/xusb-padctl.h> + +#endif /* _TEGRA20_XUSB_PADCTL_H_ */ diff --git a/arch/arm/include/asm/arch-tegra30/xusb-padctl.h b/arch/arm/include/asm/arch-tegra30/xusb-padctl.h new file mode 100644 index 000000000000..ed429963a3a5 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra30/xusb-padctl.h @@ -0,0 +1,6 @@ +#ifndef _TEGRA30_XUSB_PADCTL_H_ +#define _TEGRA30_XUSB_PADCTL_H_ + +#include <asm/arch-tegra/xusb-padctl.h> + +#endif /* _TEGRA30_XUSB_PADCTL_H_ */ diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index d01abcee13c1..ba6417c5f535 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/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 56bc29375128..c0464f07961d 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -82,6 +82,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 d2f2c9164b37..16e0714b0fed 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"),

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
arch/arm/include/asm/arch-tegra/xusb-padctl.h | 14 + arch/arm/include/asm/arch-tegra114/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra124/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra20/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra30/xusb-padctl.h | 6 +
These per-SoC headers do nothing but include the common header. Why not just have the code include the common header directly? IIRC, that's what we do now for some other modules that are identical. We only need SoC-specific headers if we have SoC-specific information to represent.
diff --git a/arch/arm/cpu/tegra124-common/Makefile b/arch/arm/cpu/tegra124-common/Makefile
obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o +obj-y += xusb-padctl.o
It looks like there's indentation inconsistency there?
diff --git a/arch/arm/cpu/tegra124-common/xusb-padctl.c b/arch/arm/cpu/tegra124-common/xusb-padctl.c
+int fdtdec_count_strings(const void *fdt, int node, const char *prop_name)
+int fdtdec_get_string_index(const void *fdt, int node, const char *prop_name,
int index, const char **output)
+int fdtdec_get_string(const void *fdt, int node, const char *prop_name,
Shouldn't those be in libfdt or similar? I thought I saw a patch to do that.
diff --git a/arch/arm/include/asm/arch-tegra/xusb-padctl.h b/arch/arm/include/asm/arch-tegra/xusb-padctl.h
+struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type);
What is "type"? A comment would be useful, or using an enum type instead.
With those fixed, I don't see anything obviously wrong, so, Acked-by: Stephen Warren swarren@nvidia.com

On Wed, Aug 20, 2014 at 12:32:45PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
arch/arm/include/asm/arch-tegra/xusb-padctl.h | 14 + arch/arm/include/asm/arch-tegra114/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra124/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra20/xusb-padctl.h | 6 + arch/arm/include/asm/arch-tegra30/xusb-padctl.h | 6 +
These per-SoC headers do nothing but include the common header. Why not just have the code include the common header directly? IIRC, that's what we do now for some other modules that are identical. We only need SoC-specific headers if we have SoC-specific information to represent.
Done.
diff --git a/arch/arm/cpu/tegra124-common/Makefile b/arch/arm/cpu/tegra124-common/Makefile
obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o +obj-y += xusb-padctl.o
It looks like there's indentation inconsistency there?
I don't have it in my local tree. But perhaps I've fixed it meanwhile without remembering?
diff --git a/arch/arm/cpu/tegra124-common/xusb-padctl.c b/arch/arm/cpu/tegra124-common/xusb-padctl.c
+int fdtdec_count_strings(const void *fdt, int node, const char *prop_name)
+int fdtdec_get_string_index(const void *fdt, int node, const char *prop_name,
int index, const char **output)
+int fdtdec_get_string(const void *fdt, int node, const char *prop_name,
Shouldn't those be in libfdt or similar? I thought I saw a patch to do that.
I'm not sure where exactly to put them. They are slightly different from the fdt_get_string_index() that's implemented in an earlier patch in that they return a pointer to the string at a given index whereas the fdt_get_string_index() function looks up a string in a list and returns its index. The naming is horrible, I know, but I'm open to suggestions.
diff --git a/arch/arm/include/asm/arch-tegra/xusb-padctl.h b/arch/arm/include/asm/arch-tegra/xusb-padctl.h
+struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type);
What is "type"? A comment would be useful, or using an enum type instead.
The value should come directly from DT, so an enum won't be very useful. It's also very much SoC dependent, so it's difficult to give an example that works for all SoCs. I'll try what I can come up with for a comment.
Thierry

On Wed, Aug 20, 2014 at 12:32:45PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
[...]
diff --git a/arch/arm/cpu/tegra124-common/xusb-padctl.c b/arch/arm/cpu/tegra124-common/xusb-padctl.c
+int fdtdec_count_strings(const void *fdt, int node, const char *prop_name)
+int fdtdec_get_string_index(const void *fdt, int node, const char *prop_name,
int index, const char **output)
+int fdtdec_get_string(const void *fdt, int node, const char *prop_name,
Shouldn't those be in libfdt or similar? I thought I saw a patch to do that.
I renamed the current fdt_get_string_index() to fdt_find_string() in the hope of removing the naming ambiguity and added these two as fdt_get_string_index() and fdt_get_string(). And I moved the fdtdec_count_strings() into libfdt as well and named it fdt_count_strings().
Thierry

From: Thierry Reding treding@nvidia.com
The XUSB pad controller is used for pinmuxing of the XUSB, PCIe and SATA lanes.
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>;

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
The XUSB pad controller is used for pinmuxing of the XUSB, PCIe and SATA lanes.
Aside from the differing #address-cells at the top-level of the DT, this matches what's been proposed for the Linux kernel, so,
Acked-by: Stephen Warren swarren@nvidia.com

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.
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 */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
Aside from the differing #address-cells at the top-level of the DT, this matches what's been proposed for the Linux kernel, so,
Acked-by: Stephen Warren swarren@nvidia.com

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.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/pci/Makefile | 1 + drivers/pci/pci_tegra.c | 1133 +++++++++++++++++++++++++++++++++++++++++++++++ include/fdtdec.h | 3 + lib/fdtdec.c | 3 + 4 files changed, 1140 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..3316ef34e092 --- /dev/null +++ b/drivers/pci/pci_tegra.c @@ -0,0 +1,1133 @@ +/* + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport mike@compulab.co.il + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, 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/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); + + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + + tegra_pcie_board_init(); + + tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + PERIPH_ID_PCIE); + + /* 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 c0464f07961d..2869cd0ae206 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -82,6 +82,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 16e0714b0fed..3e41b0c99683 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"),

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c
- Copyright (c) 2010, CompuLab, Ltd.
- Author: Mike Rapoport mike@compulab.co.il
- Based on NVIDIA PCIe driver
- Copyright (c) 2008-2009, NVIDIA Corporation.
Presumably also at least (c) 2013-2014 NVIDIA too?
Do you have Mike's S-o-b for the initial work?
+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;
- }
In the kernel, don't we have to play games to dynamically map/unmap memory regions and/or map pages in a non-linear fashion since the config space is so large, and bus/device ID -> address mapping is odd? I don't see that here.
+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;
On my board, this almost never succeeds for Jetson TK1's built-in NIC, although it works fine for the mini-PCIe slot. On the other two boards I borrowed, this succeeds almost always. I'd prefer if we could get to the bottom of this before we actually apply this, although the bug is proving quite perplexing. The mainline kernel PCIe patches have the same issue.
+static int process_nodes(const void *fdt, int nodes[], unsigned int count)
+#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);
Shouldn't that ifdef'd block be part of pci_hose_scan()?

On Wed, Aug 20, 2014 at 01:04:22PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c
- Copyright (c) 2010, CompuLab, Ltd.
- Author: Mike Rapoport mike@compulab.co.il
- Based on NVIDIA PCIe driver
- Copyright (c) 2008-2009, NVIDIA Corporation.
Presumably also at least (c) 2013-2014 NVIDIA too?
Yes.
Do you have Mike's S-o-b for the initial work?
Yes, I can take it from the kernel's git log.
+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;
- }
In the kernel, don't we have to play games to dynamically map/unmap memory regions and/or map pages in a non-linear fashion since the config space is so large, and bus/device ID -> address mapping is odd? I don't see that here.
We do that in the kernel to avoid having to I/O remap a full 256 MiB of memory even if only a single PCI bus is needed. Since memory is mapped 1:1 in U-Boot and we access physical memory "directly", we don't have to worry about being wasteful. The tegra_pcie_conf_offset() function computes the proper 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 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;
On my board, this almost never succeeds for Jetson TK1's built-in NIC, although it works fine for the mini-PCIe slot. On the other two boards I borrowed, this succeeds almost always. I'd prefer if we could get to the bottom of this before we actually apply this, although the bug is proving quite perplexing. The mainline kernel PCIe patches have the same issue.
That's indeed odd. It's possible that the powergate fix that I've applied (to wait for the partition to become powered) may improve things. I'll be pushing out my latest work shortly (I don't think I'll manage to send out the whole series for review, though), so in case you get the chance you may want to test it.
+static int process_nodes(const void *fdt, int nodes[], unsigned int count)
+#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);
Shouldn't that ifdef'd block be part of pci_hose_scan()?
Every driver does it this way, but I agree that it should presumably go in the core somewhere. I'll see if I can find a good place to do it.
Thierry

On 08/20/2014 01:04 PM, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c
+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;
On my board, this almost never succeeds for Jetson TK1's built-in NIC, although it works fine for the mini-PCIe slot. On the other two boards I borrowed, this succeeds almost always. I'd prefer if we could get to the bottom of this before we actually apply this, although the bug is proving quite perplexing. The mainline kernel PCIe patches have the same issue.
I tracked the issue down to some missing pinmux configuration for the PCIe reset lane. With that in place, both U-Boot and the mainline Linux kernel PCIe support appear to work reliably. I'll work on patches to resolve that. So, I think we can go ahead and merge these patches without issue ASAP, as soon as any review comments are addressed.

On Fri, Aug 22, 2014 at 11:33:39AM -0600, Stephen Warren wrote:
On 08/20/2014 01:04 PM, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c
+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;
On my board, this almost never succeeds for Jetson TK1's built-in NIC, although it works fine for the mini-PCIe slot. On the other two boards I borrowed, this succeeds almost always. I'd prefer if we could get to the bottom of this before we actually apply this, although the bug is proving quite perplexing. The mainline kernel PCIe patches have the same issue.
I tracked the issue down to some missing pinmux configuration for the PCIe reset lane. With that in place, both U-Boot and the mainline Linux kernel PCIe support appear to work reliably. I'll work on patches to resolve that.
Hey, that's great news.
So, I think we can go ahead and merge these patches without issue ASAP, as soon as any review comments are addressed.
Okay, I have v2 pretty much ready with all the review comments addressed and just need some time to write up a cover letter and send the series out.
Thierry

From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra20 SoCs.
Signed-off-by: Thierry Reding treding@nvidia.com --- arch/arm/dts/tegra20.dtsi | 56 +++++++++++ include/dt-bindings/clock/tegra20-car.h | 158 ++++++++++++++++++++++++++++++++ 2 files changed, 214 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..ac9aeeb76582 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,61 @@ reg = <0x7000f400 0x200>; };
+ pcie-controller { + 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"; + + 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 */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra20 SoCs.
This matches the kernel, so Acked-by: Stephen Warren swarren@nvidia.com

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 --- arch/arm/dts/tegra20-trimslice.dts | 35 +++++++++++++++++++++++++++++++++++ board/compulab/trimslice/trimslice.c | 8 ++++++++ include/configs/trimslice.h | 10 ++++++++++ 3 files changed, 53 insertions(+)
diff --git a/arch/arm/dts/tegra20-trimslice.dts b/arch/arm/dts/tegra20-trimslice.dts index ee31476c1eb8..298a0379c591 100644 --- a/arch/arm/dts/tegra20-trimslice.dts +++ b/arch/arm/dts/tegra20-trimslice.dts @@ -42,6 +42,16 @@ status = "disabled"; };
+ pcie-controller { + status = "okay"; + pex-clk-supply = <&pci_clk_reg>; + vdd-supply = <&pci_vdd_reg>; + + pci@1,0 { + status = "okay"; + }; + }; + usb@c5000000 { nvidia,vbus-gpio = <&gpio 170 0>; /* PV2 */ }; @@ -61,4 +71,29 @@ wp-gpios = <&gpio 122 0>; /* gpio PP2 */ bus-width = <4>; }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + 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

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/arch/arm/dts/tegra20-trimslice.dts b/arch/arm/dts/tegra20-trimslice.dts
- pcie-controller {
status = "okay";
pex-clk-supply = <&pci_clk_reg>;
vdd-supply = <&pci_vdd_reg>;
pci@1,0 {
status = "okay";
};
- };
There are many more regulators here in the kernel's DT. Is that just the legacy properties in order to support the old incorrect binding?

On Wed, Aug 20, 2014 at 12:38:15PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/arch/arm/dts/tegra20-trimslice.dts b/arch/arm/dts/tegra20-trimslice.dts
- pcie-controller {
status = "okay";
pex-clk-supply = <&pci_clk_reg>;
vdd-supply = <&pci_vdd_reg>;
pci@1,0 {
status = "okay";
};
- };
There are many more regulators here in the kernel's DT. Is that just the legacy properties in order to support the old incorrect binding?
Yes, I actually wrote these patches (or at least the bulk of it) many moons ago when the new binding didn't exist yet. I've updated all DTS files in U-Boot to be up-to-date with the latest bindings.
Thierry

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 --- arch/arm/dts/tegra30.dtsi | 71 +++++++++ include/dt-bindings/clock/tegra30-car.h | 265 ++++++++++++++++++++++++++++++++ 2 files changed, 336 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 7be3791fc9a6..ae0abf055a78 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>
@@ -6,6 +7,76 @@ / { compatible = "nvidia,tegra30";
+ pcie-controller { + 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"; + + 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 */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra30 SoCs.
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi
- pcie-controller {
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";
There are interrupt-map and interrupt-map-mask properties here in the kernel DT. Even if they aren't used, can we add them here to keep the two consistent?

On Wed, Aug 20, 2014 at 12:39:14PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra30 SoCs.
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi
- pcie-controller {
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";
There are interrupt-map and interrupt-map-mask properties here in the kernel DT. Even if they aren't used, can we add them here to keep the two consistent?
I had omitted them to avoid having to bring in the GIC node, but I'll do that instead so that the device trees actually start converging.
Thierry

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 --- arch/arm/dts/tegra30-beaver.dts | 166 ++++++++++++++++++++++++++++++++++++++++ board/nvidia/cardhu/cardhu.c | 56 ++++++++++++++ include/configs/beaver.h | 10 +++ 3 files changed, 232 insertions(+)
diff --git a/arch/arm/dts/tegra30-beaver.dts b/arch/arm/dts/tegra30-beaver.dts index 85e62e9db32e..e113801fdfb4 100644 --- a/arch/arm/dts/tegra30-beaver.dts +++ b/arch/arm/dts/tegra30-beaver.dts @@ -23,6 +23,27 @@ reg = <0x80000000 0x7ff00000>; };
+ pcie-controller { + status = "okay"; + pex-clk-supply = <&sys_3v3_pexs_reg>; + vdd-supply = <&ldo1_reg>; + avdd-supply = <&ldo2_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 +67,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 +206,45 @@ 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; + }; + + 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>; + }; + }; }; diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index cc0e5e130fda..5738dd1f5195 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_read(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/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

- /* TPS659110: LDO2_REG = 1.05V, ACTIVE */
- data[0] = 0x15;
- addr = 0x31;
- err = i2c_read(PMU_I2C_ADDRESS, addr, 1, data, 1);
You sure about that read?
Me wondering why it did not work on my shiny Apalis T30.
- if (err) {
debug("failed to set AVDD supply\n");
return err;
- }
Yeah, that one just did not wana set anything (;-p).

On Tue, Aug 19, 2014 at 01:48:55PM +0000, Marcel Ziswiler wrote:
- /* TPS659110: LDO2_REG = 1.05V, ACTIVE */
- data[0] = 0x15;
- addr = 0x31;
- err = i2c_read(PMU_I2C_ADDRESS, addr, 1, data, 1);
You sure about that read?
Me wondering why it did not work on my shiny Apalis T30.
- if (err) {
debug("failed to set AVDD supply\n");
return err;
- }
Yeah, that one just did not wana set anything (;-p).
Hehe... this should probably be i2c_write() instead. Perhaps this is on by default on Beaver and Cardhu but not on the particular revision that Apalis T30 uses?
Can you test whether things start working if you turn that into a write instead?
Thierry

On Wed, 2014-08-20 at 08:38 +0200, Thierry Reding wrote:
Hehe... this should probably be i2c_write() instead. Perhaps this is on by default on Beaver and Cardhu but not on the particular revision that Apalis T30 uses?
Well, we anyway use a completely different rail configuration but me just copy-pasting your read and only changing register addresses resp. values happens to just not wana be working (;-p).
Can you test whether things start working if you turn that into a write instead?
Sure, already did and it works like a beauty! You're the man!
U-Boot SPL 2014.10-rc1-00110-gff50ce9-dirty (Aug 20 2014 - 10:14:49)
U-Boot 2014.10-rc1-00110-gff50ce9-dirty (Aug 20 2014 - 10:14:49)
TEGRA30 Board: Toradex Apalis T30 I2C: ready DRAM: 1 GiB MMC: Tegra SD/MMC: 0, Tegra SD/MMC: 1, Tegra SD/MMC: 2 *** Warning - bad CRC, using default environment
tegra-pcie: PCI regions: tegra-pcie: I/O: 0x2000000-0x2010000 tegra-pcie: non-prefetchable memory: 0x20000000-0x30000000 tegra-pcie: prefetchable memory: 0x30000000-0x40000000 tegra-pcie: 4x1, 1x2 configuration tegra-pcie: probing port 0, using 4 lanes tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, ignoring tegra-pcie: probing port 1, using 1 lanes tegra-pcie: probing port 2, using 1 lanes In: serial Out: serial Err: serial Net: PCI device RTL8169#0: unknown chip version, assuming RTL-8169 PCI device: TxConfig = 0x3F4000C0 RTL8169#0 Warning: RTL8169#0 using MAC address from net device
Hit any key to stop autoboot: 0 Apalis T30 # dhcp BOOTP broadcast 1 DHCP client bound to address 192.168.10.5 Using RTL8169#0 device TFTP from server 192.168.10.1; our IP address is 192.168.10.5 Filename 'uImage-apalis_t30'. Load address: 0x80408000 Loading: #################################################################
#################################################################
#################################################################
################################################################# #################################################### 3.9 MiB/s done Bytes transferred = 4569784 (45bab8 hex) Apalis T30 #

On Wed, Aug 20, 2014 at 10:56:58AM +0200, Marcel Ziswiler wrote:
On Wed, 2014-08-20 at 08:38 +0200, Thierry Reding wrote:
Hehe... this should probably be i2c_write() instead. Perhaps this is on by default on Beaver and Cardhu but not on the particular revision that Apalis T30 uses?
Well, we anyway use a completely different rail configuration but me just copy-pasting your read and only changing register addresses resp. values happens to just not wana be working (;-p).
Can you test whether things start working if you turn that into a write instead?
Sure, already did and it works like a beauty! You're the man!
Pretty cool.
U-Boot SPL 2014.10-rc1-00110-gff50ce9-dirty (Aug 20 2014 - 10:14:49)
U-Boot 2014.10-rc1-00110-gff50ce9-dirty (Aug 20 2014 - 10:14:49)
TEGRA30 Board: Toradex Apalis T30 I2C: ready DRAM: 1 GiB MMC: Tegra SD/MMC: 0, Tegra SD/MMC: 1, Tegra SD/MMC: 2 *** Warning - bad CRC, using default environment
tegra-pcie: PCI regions: tegra-pcie: I/O: 0x2000000-0x2010000 tegra-pcie: non-prefetchable memory: 0x20000000-0x30000000 tegra-pcie: prefetchable memory: 0x30000000-0x40000000 tegra-pcie: 4x1, 1x2 configuration tegra-pcie: probing port 0, using 4 lanes tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, retrying tegra-pcie: link 0 down, ignoring tegra-pcie: probing port 1, using 1 lanes tegra-pcie: probing port 2, using 1 lanes In: serial Out: serial Err: serial Net: PCI device RTL8169#0: unknown chip version, assuming RTL-8169 PCI device: TxConfig = 0x3F4000C0 RTL8169#0 Warning: RTL8169#0 using MAC address from net device
Hit any key to stop autoboot: 0 Apalis T30 # dhcp BOOTP broadcast 1 DHCP client bound to address 192.168.10.5 Using RTL8169#0 device TFTP from server 192.168.10.1; our IP address is 192.168.10.5 Filename 'uImage-apalis_t30'. Load address: 0x80408000 Loading: #################################################################
#################################################################
#################################################################
################################################################# #################################################### 3.9 MiB/s done Bytes transferred = 4569784 (45bab8 hex) Apalis T30 #
Wow, 3.9 MiB/s on average. I only get around 2.0 MiB/s on all my devices. TrimSlice is the exception with around 5.0 MiB/s. I wonder where exactly that comes from. It's the oldest device so I'd expect it to be the slowest too.
Thierry

On Wed, 2014-08-20 at 11:46 +0200, Thierry Reding wrote:
Wow, 3.9 MiB/s on average. I only get around 2.0 MiB/s on all my devices. TrimSlice is the exception with around 5.0 MiB/s. I wonder where exactly that comes from. It's the oldest device so I'd expect it to be the slowest too.
That's now with Marek's i210 patches plus some more fiddling on my side. Not perfect yet but already pretty blazing fast, sorry about that (;-p).
BTW: You do have our hw laying around as well don't you. So no need to play with this slow Jetson TK1 stuff any longer (;-p).
Apalis T30 # dhcp e1000: e1000#0: ERROR: Hardware Initialization Failed BOOTP broadcast 1 DHCP client bound to address 192.168.10.5 Using e1000#0 device TFTP from server 192.168.10.1; our IP address is 192.168.10.5 Filename 'uImage-apalis_t30'. Load address: 0x80408000 Loading: #################################################################
#################################################################
#################################################################
################################################################# #################################################### 7.3 MiB/s done Bytes transferred = 4569784 (45bab8 hex)

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/arch/arm/dts/tegra30-beaver.dts b/arch/arm/dts/tegra30-beaver.dts
- pcie-controller {
status = "okay";
pex-clk-supply = <&sys_3v3_pexs_reg>;
vdd-supply = <&ldo1_reg>;
avdd-supply = <&ldo2_reg>;
Just like TrimSlice, there are more regulator references in the kernel DT.
The same comment applies to the next patch (i.e. the Cardhu DT patch)
- regulators {
...
vdd_5v_in_reg: regulator@0 {
...
sys_3v3_reg: regulator@6 {
...
sys_3v3_pexs_reg: regulator@7 {
This only adds 3 of the regulators that are in the kernel DT. Can we just add the whole regulators node so the two are consistent?
diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c
The commit description says Beaver...
I guess this should be part of the next patch, and the two swapped?

On Wed, Aug 20, 2014 at 12:43:58PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/arch/arm/dts/tegra30-beaver.dts b/arch/arm/dts/tegra30-beaver.dts
- pcie-controller {
status = "okay";
pex-clk-supply = <&sys_3v3_pexs_reg>;
vdd-supply = <&ldo1_reg>;
avdd-supply = <&ldo2_reg>;
Just like TrimSlice, there are more regulator references in the kernel DT.
The same comment applies to the next patch (i.e. the Cardhu DT patch)
Done. I also added the unit address to the PCIe controller node name for consistency.
- regulators {
...
vdd_5v_in_reg: regulator@0 {
...
sys_3v3_reg: regulator@6 {
...
sys_3v3_pexs_reg: regulator@7 {
This only adds 3 of the regulators that are in the kernel DT. Can we just add the whole regulators node so the two are consistent?
Done.
diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c
The commit description says Beaver...
I guess this should be part of the next patch, and the two swapped?
Done.
Thanks, Thierry

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 --- arch/arm/dts/tegra30-cardhu.dts | 357 ++++++++++++++++++++++++++++++++++++++++ include/configs/cardhu.h | 10 ++ 2 files changed, 367 insertions(+)
diff --git a/arch/arm/dts/tegra30-cardhu.dts b/arch/arm/dts/tegra30-cardhu.dts index ea2cf76ff3a8..8d3e85ab263b 100644 --- a/arch/arm/dts/tegra30-cardhu.dts +++ b/arch/arm/dts/tegra30-cardhu.dts @@ -22,6 +22,26 @@ reg = <0x80000000 0x40000000>; };
+ pcie-controller { + status = "okay"; + pex-clk-supply = <&pex_hvdd_3v3_reg>; + vdd-supply = <&ldo1_reg>; + avdd-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 +65,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 +190,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/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
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.
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";

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
Matches the kernel, so, Acked-by: Stephen Warren swarren@nvidia.com

From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra124 SoCs.
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 */

On 08/18/2014 01:16 AM, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
Add the device tree node for the PCIe controller found on Tegra124 SoCs.
This matches what's proposed for the kernel, so, Acked-by: Stephen Warren swarren@nvidia.com

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 --- arch/arm/dts/tegra124-jetson-tk1.dts | 347 +++++++++++++++++++++++++++++++++++ board/nvidia/jetson-tk1/jetson-tk1.c | 218 ++++++++++++++++++++++ include/configs/jetson-tk1.h | 10 + 3 files changed, 575 insertions(+)
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts index f61736f0ef0f..f594644de74e 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>; + vddio-pex-supply = <&vdd_1v05_run>; + avdd-pex-pll-supply = <&vdd_1v05_run>; + hvdd-pex-supply = <&vdd_3v3_lp0>; + hvdd-pex-plle-supply = <&vdd_3v3_lp0>; + vddio-pex-ctl-supply = <&vdd_3v3_lp0>; + avdd-plle-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..c1deaa11d63e 100644 --- a/board/nvidia/jetson-tk1/jetson-tk1.c +++ b/board/nvidia/jetson-tk1/jetson-tk1.c @@ -6,9 +6,25 @@ */
#include <common.h> +#include <errno.h> #include <asm/arch/gpio.h> #include <asm/arch/pinmux.h> #include "pinmux-config-jetson-tk1.h" +#include <i2c.h> +#include <netdev.h> + +#define PMU_I2C_ADDRESS 0x40 +#define AS3722_DEVICE_ID 0x0c + +#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_GPIO_SIGNAL_OUT 0x20 +#define AS3722_SD_CONTROL 0x4d +#define AS3722_ASIC_ID1 0x90 +#define AS3722_ASIC_ID2 0x91
/* * Routine: pinmux_init @@ -27,3 +43,205 @@ void pinmux_init(void) pinmux_config_drvgrp_table(jetson_tk1_drvgrps, ARRAY_SIZE(jetson_tk1_drvgrps)); } + +#ifdef CONFIG_PCI_TEGRA +static int as3722_read(u8 reg, u8 *value) +{ + int err; + + err = i2c_read(PMU_I2C_ADDRESS, reg, 1, value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_write(u8 reg, u8 value) +{ + int err; + + err = i2c_write(PMU_I2C_ADDRESS, reg, 1, &value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_read_id(u8 *id, u8 *revision) +{ + int err; + + err = as3722_read(AS3722_ASIC_ID1, id); + if (err) { + error("as3722: failed to read ID1 register: %d\n", err); + return err; + } + + err = as3722_read(AS3722_ASIC_ID2, revision); + if (err) { + error("as3722: failed to read ID2 register: %d\n", err); + return err; + } + + return 0; +} + +static int as3722_sd_enable(u8 sd) +{ + u8 value; + int err; + + err = as3722_read(AS3722_SD_CONTROL, &value); + if (err) { + error("as3722: failed to read SD control register: %d\n", err); + return err; + } + + value |= 1 << sd; + + err = as3722_write(AS3722_SD_CONTROL, value); + if (err < 0) { + error("as3722: failed to write SD control register: %d\n", err); + return err; + } + + return 0; +} + +static int as3722_sd_set_voltage(u8 sd, u8 value) +{ + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_write(AS3722_SD_VOLTAGE(sd), value); + if (err < 0) { + error("as3722: failed to write SD%u voltage register: %d\n", sd, err); + return err; + } + + return 0; +} + +static int as3722_gpio_set(u8 gpio, u8 level) +{ + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + err = as3722_read(AS3722_GPIO_SIGNAL_OUT, &value); + if (err < 0) { + error("as3722: failed to read GPIO signal out register: %d\n", + err); + return err; + } + + if (level == 0) + value &= ~(1 << gpio); + else + value |= 1 << gpio; + + err = as3722_write(AS3722_GPIO_SIGNAL_OUT, value); + if (err) { + error("as3722: failed to set GPIO#%u %s: %d\n", gpio, + (level == 0) ? "low" : "high", err); + return err; + } + + return 0; +} + +static int as3722_gpio_direction_output(u8 gpio, u8 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(AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("as3722: failed to configure GPIO#%u as output: %d\n", + gpio, err); + return err; + } + + err = as3722_gpio_set(gpio, level); + if (err < 0) { + error("as3722: failed to set GPIO#%u high: %d\n", gpio, err); + return err; + } + + return 0; +} + +int tegra_pcie_board_init(void) +{ + u8 id, revision, value; + unsigned int old_bus; + int err; + + old_bus = i2c_get_bus_num(); + + err = i2c_set_bus_num(0); + if (err) { + error("failed to set I2C bus\n"); + return err; + } + + err = as3722_read_id(&id, &revision); + if (err < 0) { + error("as3722: failed to read ID: %d\n", err); + return err; + } + + if (id != AS3722_DEVICE_ID) { + error("as3722: PMIC is not an AS3722\n"); + return -ENODEV; + } + + err = as3722_sd_enable(4); + if (err < 0) { + error("as3722: failed to enable SD4: %d\n", err); + return err; + } + + err = as3722_sd_set_voltage(4, 0x24); + if (err < 0) { + error("as3722: failed to set SD4 voltage: %d\n", err); + return err; + } + + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH | + AS3722_GPIO_CONTROL_INVERT; + + err = as3722_write(AS3722_GPIO_CONTROL(1), value); + if (err) { + error("as3722: failed to configure GPIO#1 as output: %d\n", err); + return err; + } + + err = as3722_gpio_direction_output(2, 1); + if (err < 0) { + error("as3722: failed to set GPIO#2 high: %d\n", err); + 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/jetson-tk1.h b/include/configs/jetson-tk1.h index 0b9e5b699fa6..b0393509681c 100644 --- a/include/configs/jetson-tk1.h +++ b/include/configs/jetson-tk1.h @@ -71,6 +71,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

Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Cool, I'll have to give this a try! Shouldn't the as3722 code be in its own driver?
Regards, Simon

On Mon, Aug 18, 2014 at 12:37:46PM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Cool, I'll have to give this a try! Shouldn't the as3722 code be in its own driver?
I suppose it could. My intention was to move it out at some point when we needed it for another device. But if you prefer I can move it out in this series already.
Thierry

Hi Thierry,
On 19 August 2014 06:29, Thierry Reding thierry.reding@gmail.com wrote:
On Mon, Aug 18, 2014 at 12:37:46PM -0600, Simon Glass wrote:
Hi Thierry,
On 18 August 2014 01:16, Thierry Reding thierry.reding@gmail.com wrote:
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
Cool, I'll have to give this a try! Shouldn't the as3722 code be in its own driver?
I suppose it could. My intention was to move it out at some point when we needed it for another device. But if you prefer I can move it out in this series already.
Yes I think we should avoid putting driver code in board files.
BTW there is:
i2c_bus = i2c_get_bus_num_fdt(parent);
where parent is the fdt node of the parent. So you don't have to hard-code the bus number. See tpm_decode_config() for an example.
Regards, Simon

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts
- regulators {
/* Molex power connector */
vdd_5v0_sata: regulator@13 {
...
vdd_12v0_sata: regulator@14 {
Those two aren't in the kernel DT yet. I'll assume they match what's been proposed for the SATA patches:-)
diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c b/board/nvidia/jetson-tk1/jetson-tk1.c
A lot of the AS3722 code here duplicates what's in board/nvidia/venice2/as3722_init.[ch]. It'd be good to put it all in one place. Personally I'd be OK with putting it into the existing Venice2 code, but I suppose as Simon mentioned, a "real" AS3722 driver in drivers/power/pmic would be a good idea.

On Wed, Aug 20, 2014 at 12:51:35PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts
- regulators {
/* Molex power connector */
vdd_5v0_sata: regulator@13 {
...
vdd_12v0_sata: regulator@14 {
Those two aren't in the kernel DT yet. I'll assume they match what's been proposed for the SATA patches:-)
diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c b/board/nvidia/jetson-tk1/jetson-tk1.c
A lot of the AS3722 code here duplicates what's in board/nvidia/venice2/as3722_init.[ch]. It'd be good to put it all in one place. Personally I'd be OK with putting it into the existing Venice2 code, but I suppose as Simon mentioned, a "real" AS3722 driver in drivers/power/pmic would be a good idea.
Unfortunately turning this into a real driver for AS3722 makes it impossible (or at least too difficult) to unify with the code in venice2/as3722_init.ch. The issue is that the new driver relies on infrastructure (I2C, FDT) that's not available in SPL (where this code is run). I tried extending the SPL with I2C and power drivers support so that I could use the driver here as well, but that keeps failing at various points, so I preferred having a proper driver for full U-Boot and kept the low-level code for CPU power rail setup as is.
Note that I haven't turned the AS3722 support into a "PMIC" driver, because the framework for that seems to be unusable. It doesn't seem to abstract away driver-specifics at all but rather provides a way to access registers in a uniform way (sort of what regmap does in Linux). Using that framework would therefore require knowledge about the exact register accesses within drivers and therefore wouldn't be an improvement over the current situation.
Thierry

On 08/22/2014 06:09 AM, Thierry Reding wrote:
On Wed, Aug 20, 2014 at 12:51:35PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts
- regulators {
/* Molex power connector */
vdd_5v0_sata: regulator@13 {
...
vdd_12v0_sata: regulator@14 {
Those two aren't in the kernel DT yet. I'll assume they match what's been proposed for the SATA patches:-)
diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c b/board/nvidia/jetson-tk1/jetson-tk1.c
A lot of the AS3722 code here duplicates what's in board/nvidia/venice2/as3722_init.[ch]. It'd be good to put it all in one place. Personally I'd be OK with putting it into the existing Venice2 code, but I suppose as Simon mentioned, a "real" AS3722 driver in drivers/power/pmic would be a good idea.
Unfortunately turning this into a real driver for AS3722 makes it impossible (or at least too difficult) to unify with the code in venice2/as3722_init.ch. The issue is that the new driver relies on infrastructure (I2C, FDT) that's not available in SPL (where this code is run). I tried extending the SPL with I2C and power drivers support so that I could use the driver here as well, but that keeps failing at various points, so I preferred having a proper driver for full U-Boot and kept the low-level code for CPU power rail setup as is.
Ah yes. That makes sense.
Note that I haven't turned the AS3722 support into a "PMIC" driver, because the framework for that seems to be unusable. It doesn't seem to abstract away driver-specifics at all but rather provides a way to access registers in a uniform way (sort of what regmap does in Linux). Using that framework would therefore require knowledge about the exact register accesses within drivers and therefore wouldn't be an improvement over the current situation.
Thierry

Hi Thierry,
On 22 August 2014 06:09, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Aug 20, 2014 at 12:51:35PM -0600, Stephen Warren wrote:
On 08/18/2014 01:16 AM, Thierry Reding wrote:
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
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts
b/arch/arm/dts/tegra124-jetson-tk1.dts
- regulators {
/* Molex power connector */
vdd_5v0_sata: regulator@13 {
...
vdd_12v0_sata: regulator@14 {
Those two aren't in the kernel DT yet. I'll assume they match what's been proposed for the SATA patches:-)
diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c
b/board/nvidia/jetson-tk1/jetson-tk1.c
A lot of the AS3722 code here duplicates what's in board/nvidia/venice2/as3722_init.[ch]. It'd be good to put it all in one place. Personally I'd be OK with putting it into the existing Venice2
code,
but I suppose as Simon mentioned, a "real" AS3722 driver in drivers/power/pmic would be a good idea.
Unfortunately turning this into a real driver for AS3722 makes it impossible (or at least too difficult) to unify with the code in venice2/as3722_init.ch. The issue is that the new driver relies on infrastructure (I2C, FDT) that's not available in SPL (where this code is run). I tried extending the SPL with I2C and power drivers support so that I could use the driver here as well, but that keeps failing at various points, so I preferred having a proper driver for full U-Boot and kept the low-level code for CPU power rail setup as is.
That seems OK to me. When we have driver model in there we can perhaps revisit things.
Note that I haven't turned the AS3722 support into a "PMIC" driver, because the framework for that seems to be unusable. It doesn't seem to abstract away driver-specifics at all but rather provides a way to access registers in a uniform way (sort of what regmap does in Linux). Using that framework would therefore require knowledge about the exact register accesses within drivers and therefore wouldn't be an improvement over the current situation.
It doesn't provide a PMIC API as such - just a way to find a PMIC and the access registers. I think it is useful for that at least.
Regards, Simon

On Fri, Aug 22, 2014 at 01:27:57PM -0600, Simon Glass wrote:
On 22 August 2014 06:09, Thierry Reding thierry.reding@gmail.com wrote:
[...]
Note that I haven't turned the AS3722 support into a "PMIC" driver, because the framework for that seems to be unusable. It doesn't seem to abstract away driver-specifics at all but rather provides a way to access registers in a uniform way (sort of what regmap does in Linux). Using that framework would therefore require knowledge about the exact register accesses within drivers and therefore wouldn't be an improvement over the current situation.
It doesn't provide a PMIC API as such - just a way to find a PMIC and the access registers. I think it is useful for that at least.
But that's not very useful in itself, is it? I mean, there's no abstraction whatsoever for the PMIC functionality, so all users will need to implement direct register accesses, which essentially means there's about zero code reuse. So the abstraction is at the wrong point in my opinion and the only use-case where I think it would be beneficial is if the same PMIC could be used on different interfaces (I2C vs. SPI) and therefore the register access abstraction could allow a single driver to control both types.
I've opted instead to provide an somewhat higher-level API that users can call to set voltages on the regulators and enable them.
Thierry

Hi Thierry,
On 22 August 2014 13:40, Thierry Reding thierry.reding@gmail.com wrote:
On Fri, Aug 22, 2014 at 01:27:57PM -0600, Simon Glass wrote:
On 22 August 2014 06:09, Thierry Reding thierry.reding@gmail.com wrote:
[...]
Note that I haven't turned the AS3722 support into a "PMIC" driver, because the framework for that seems to be unusable. It doesn't seem to abstract away driver-specifics at all but rather provides a way to access registers in a uniform way (sort of what regmap does in Linux). Using that framework would therefore require knowledge about the exact register accesses within drivers and therefore wouldn't be an improvement over the current situation.
It doesn't provide a PMIC API as such - just a way to find a PMIC and the access registers. I think it is useful for that at least.
But that's not very useful in itself, is it? I mean, there's no abstraction whatsoever for the PMIC functionality, so all users will need to implement direct register accesses, which essentially means there's about zero code reuse. So the abstraction is at the wrong point in my opinion and the only use-case where I think it would be beneficial is if the same PMIC could be used on different interfaces (I2C vs. SPI) and therefore the register access abstraction could allow a single driver to control both types.
Yes that's right.
I've opted instead to provide an somewhat higher-level API that users can call to set voltages on the regulators and enable them.
But then this should use/extend the pmic interface I think, and not create a parallel one.
Regards, Simon

On Fri, Aug 22, 2014 at 02:12:19PM -0600, Simon Glass wrote:
On 22 August 2014 13:40, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I've opted instead to provide an somewhat higher-level API that users can call to set voltages on the regulators and enable them.
But then this should use/extend the pmic interface I think, and not create a parallel one.
It's not a parallel framework. And it's not anything out of the ordinary either. There's a whole bunch of drivers in drivers/power that do the very same thing.
And I'm not sure something like Linux' regulator framework is something that we really need in U-Boot. The code in question is usually run in some board-specific initialization file, not from some generic driver that would need to be used in conjunction with potentially very many PMICs.
Thierry

Hi Thierry,
On 22 August 2014 16:03, Thierry Reding thierry.reding@gmail.com wrote:
On Fri, Aug 22, 2014 at 02:12:19PM -0600, Simon Glass wrote:
On 22 August 2014 13:40, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I've opted instead to provide an somewhat higher-level API that users can call to set voltages on the regulators and enable them.
But then this should use/extend the pmic interface I think, and not create a parallel one.
It's not a parallel framework. And it's not anything out of the ordinary either. There's a whole bunch of drivers in drivers/power that do the very same thing.
And I'm not sure something like Linux' regulator framework is something that we really need in U-Boot. The code in question is usually run in some board-specific initialization file, not from some generic driver that would need to be used in conjunction with potentially very many PMICs.
OK, well I suggest first take a look at pmic_tps65090.c and convince yourself that you can't plumb your pmic in in a similar way. It's up to you.
Regards, Simon

On Fri, Aug 22, 2014 at 07:47:36PM -0600, Simon Glass wrote:
Hi Thierry,
On 22 August 2014 16:03, Thierry Reding thierry.reding@gmail.com wrote:
On Fri, Aug 22, 2014 at 02:12:19PM -0600, Simon Glass wrote:
On 22 August 2014 13:40, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I've opted instead to provide an somewhat higher-level API that users can call to set voltages on the regulators and enable them.
But then this should use/extend the pmic interface I think, and not create a parallel one.
It's not a parallel framework. And it's not anything out of the ordinary either. There's a whole bunch of drivers in drivers/power that do the very same thing.
And I'm not sure something like Linux' regulator framework is something that we really need in U-Boot. The code in question is usually run in some board-specific initialization file, not from some generic driver that would need to be used in conjunction with potentially very many PMICs.
OK, well I suggest first take a look at pmic_tps65090.c and convince yourself that you can't plumb your pmic in in a similar way. It's up to you.
It's very similar to that driver except that it doesn't go through the trouble of "registering" the PMIC. Instead it works roughly like this:
struct as3722_pmic *pmic;
err = as3722_init(&pmic, fdt); if (err < 0) return err;
err = as3722_sd_set_voltage(pmic, ...); if (err < 0) return err;
err = as3722_sd_enable(pmic, ...); if (err < 0) return err;
That saves the public API from having to repeatedly go and look up the PMIC in the registry.
Thierry

On 08/18/2014 01:16 AM, Thierry Reding wrote:
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.
diff --git a/arch/arm/dts/tegra124-jetson-tk1.dts b/arch/arm/dts/tegra124-jetson-tk1.dts
i2c@7000d000 {
pmic: pmic@40 {
regulators {
sd1 {
ams,ext-control = <1>;
According to an internal bug, we need to remove that property in order to get correct sequencing of the power rails at shutdown time. We need to fix that for the kernel too. However, we need to do more than just remove that property, or suspend won't actually turn off the rails:-/ So I suppose we should wait until the kernel patches land before resolving that; your patch does match what the kernel does currently.

On 18/08/14 10:16, Thierry Reding wrote: [...]
+static int as3722_gpio_direction_output(u8 gpio, u8 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(AS3722_GPIO_CONTROL(gpio), value);
- if (err) {
error("as3722: failed to configure GPIO#%u as output: %d\n",
gpio, err);
return err;
- }
- err = as3722_gpio_set(gpio, level);
- if (err < 0) {
error("as3722: failed to set GPIO#%u high: %d\n", gpio, err);
return err;
- }
- return 0;
+}
This function doesn't work correctly if the GPIO was originally configured as inverted and low, which GPIO#2 seems to be. (as3722_read(AS3722_GPIO_CONTROL(2), &value) returns value == 0x87)...
+int tegra_pcie_board_init(void) +{
[...]
- err = as3722_gpio_direction_output(2, 1);
- if (err < 0) {
error("as3722: failed to set GPIO#2 high: %d\n", err);
return err;
- }
[...]
On my board, this call results in UART corruption, like this:
tegra-pcie: non-prefetchable memory: 0x13000000-0x20000000 tegra-pcie: prefetchable memory: 0x20000000-0x40000000 ¥É½¥¹½bªÍ¥¹b2x1, 1x1 configuration ¹Í5Rþtegra-pcie: probing port 1, using 1 lanes
Likely because GPIO#2 controls the +3.3V_LP0 rail, which powers the UART level shifters. Commenting the function call out fixes the corruption and PCI-E still works fine.

On Tue, Aug 26, 2014 at 03:54:50PM +0300, Tuomas Tynkkynen wrote:
On 18/08/14 10:16, Thierry Reding wrote: [...]
+static int as3722_gpio_direction_output(u8 gpio, u8 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(AS3722_GPIO_CONTROL(gpio), value);
- if (err) {
error("as3722: failed to configure GPIO#%u as output: %d\n",
gpio, err);
return err;
- }
- err = as3722_gpio_set(gpio, level);
- if (err < 0) {
error("as3722: failed to set GPIO#%u high: %d\n", gpio, err);
return err;
- }
- return 0;
+}
This function doesn't work correctly if the GPIO was originally configured as inverted and low, which GPIO#2 seems to be. (as3722_read(AS3722_GPIO_CONTROL(2), &value) returns value == 0x87)...
That should be equivalent to what we're setting but is a somewhat weird default. I guess the fact that we're inverting it and then changing the value to high in separate transactions makes the output flip twice.
+int tegra_pcie_board_init(void) +{
[...]
- err = as3722_gpio_direction_output(2, 1);
- if (err < 0) {
error("as3722: failed to set GPIO#2 high: %d\n", err);
return err;
- }
[...]
On my board, this call results in UART corruption, like this:
tegra-pcie: non-prefetchable memory: 0x13000000-0x20000000 tegra-pcie: prefetchable memory: 0x20000000-0x40000000 ¥É½¥¹½bªÍ¥¹b2x1, 1x1 configuration ¹Í5Rþtegra-pcie: probing port 1, using 1 lanes
Likely because GPIO#2 controls the +3.3V_LP0 rail, which powers the UART level shifters. Commenting the function call out fixes the corruption and PCI-E still works fine.
If I add a udelay(500) after the above I'm not able to reproduce the UART breakage anymore. But I guess making the AS3722 GPIO code smarter would be helpful. In the kernel this is done by checking the invert bit and then setting the value accordingly. I suppose the same could be done for the mode bits. I'll see if I can work up a patch.
Thierry

On Wed, Aug 27, 2014 at 03:28:06PM +0200, Thierry Reding wrote:
On Tue, Aug 26, 2014 at 03:54:50PM +0300, Tuomas Tynkkynen wrote:
On 18/08/14 10:16, Thierry Reding wrote: [...]
+static int as3722_gpio_direction_output(u8 gpio, u8 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(AS3722_GPIO_CONTROL(gpio), value);
- if (err) {
error("as3722: failed to configure GPIO#%u as output: %d\n",
gpio, err);
return err;
- }
- err = as3722_gpio_set(gpio, level);
- if (err < 0) {
error("as3722: failed to set GPIO#%u high: %d\n", gpio, err);
return err;
- }
- return 0;
+}
This function doesn't work correctly if the GPIO was originally configured as inverted and low, which GPIO#2 seems to be. (as3722_read(AS3722_GPIO_CONTROL(2), &value) returns value == 0x87)...
That should be equivalent to what we're setting but is a somewhat weird default. I guess the fact that we're inverting it and then changing the value to high in separate transactions makes the output flip twice.
+int tegra_pcie_board_init(void) +{
[...]
- err = as3722_gpio_direction_output(2, 1);
- if (err < 0) {
error("as3722: failed to set GPIO#2 high: %d\n", err);
return err;
- }
[...]
On my board, this call results in UART corruption, like this:
tegra-pcie: non-prefetchable memory: 0x13000000-0x20000000 tegra-pcie: prefetchable memory: 0x20000000-0x40000000 ¥É½¥¹½bªÍ¥¹b2x1, 1x1 configuration ¹Í5Rþtegra-pcie: probing port 1, using 1 lanes
Likely because GPIO#2 controls the +3.3V_LP0 rail, which powers the UART level shifters. Commenting the function call out fixes the corruption and PCI-E still works fine.
If I add a udelay(500) after the above I'm not able to reproduce the UART breakage anymore. But I guess making the AS3722 GPIO code smarter would be helpful. In the kernel this is done by checking the invert bit and then setting the value accordingly. I suppose the same could be done for the mode bits. I'll see if I can work up a patch.
How about this:
diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 59d1bf1b50b0..393dc8608d07 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -17,6 +17,7 @@ #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_MODE_MASK (7 << 0) #define AS3722_GPIO_CONTROL_INVERT (1 << 7) #define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) #define AS3722_GPIO_SIGNAL_OUT 0x20 @@ -220,10 +221,21 @@ int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio, if (gpio > 7) return -EINVAL;
+ err = as3722_read(pmic, AS3722_GPIO_CONTROL(gpio), &value); + if (err < 0) { + error("failed to read GPIO#%u control register: %d", gpio, err); + return err; + } + + if (value & AS3722_GPIO_CONTROL_INVERT) + level = !level; + + value &= ~AS3722_GPIO_CONTROL_MODE_MASK; + if (level == 0) - value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; else - value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH;
err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); if (err) {

On 27/08/14 17:34, Thierry Reding wrote: [...]
On my board, this call results in UART corruption, like this:
tegra-pcie: non-prefetchable memory: 0x13000000-0x20000000 tegra-pcie: prefetchable memory: 0x20000000-0x40000000 ¥É½¥¹½bªÍ¥¹b2x1, 1x1 configuration ¹Í5Rþtegra-pcie: probing port 1, using 1 lanes
Likely because GPIO#2 controls the +3.3V_LP0 rail, which powers the UART level shifters. Commenting the function call out fixes the corruption and PCI-E still works fine.
If I add a udelay(500) after the above I'm not able to reproduce the UART breakage anymore. But I guess making the AS3722 GPIO code smarter would be helpful. In the kernel this is done by checking the invert bit and then setting the value accordingly. I suppose the same could be done for the mode bits. I'll see if I can work up a patch.
How about this:
[...]
Yes, that helps.
participants (5)
-
Marcel Ziswiler
-
Simon Glass
-
Stephen Warren
-
Thierry Reding
-
Tuomas Tynkkynen