diff mbox

[U-Boot,v2,04/11] fdt: Add device tree memory bindings

Message ID 1414112337-22735-4-git-send-email-sjg@chromium.org
State Deferred
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass Oct. 24, 2014, 12:58 a.m. UTC
From: Michael Pratt <mpratt@chromium.org>

Support a default memory bank, specified in reg, as well as
board-specific memory banks in subtree board-id nodes.

This allows memory information to be provided in the device tree,
rather than hard-coded in, which will make it simpler to handle
similar devices with different memory banks, as the board-id values
or masks can be used to match devices.

Signed-off-by: Michael Pratt <mpratt@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
---

Changes in v2: None

 common/fdt_support.c                       |   2 +-
 doc/device-tree-bindings/memory/memory.txt |  67 ++++++++++++++++++
 include/fdtdec.h                           |  33 +++++++++
 lib/fdtdec.c                               | 107 +++++++++++++++++++++++++++++
 4 files changed, 208 insertions(+), 1 deletion(-)
 create mode 100644 doc/device-tree-bindings/memory/memory.txt

Comments

Tom Rini Oct. 24, 2014, 6:49 p.m. UTC | #1
On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:

> From: Michael Pratt <mpratt@chromium.org>
> 
> Support a default memory bank, specified in reg, as well as
> board-specific memory banks in subtree board-id nodes.
> 
> This allows memory information to be provided in the device tree,
> rather than hard-coded in, which will make it simpler to handle
> similar devices with different memory banks, as the board-id values
> or masks can be used to match devices.
[snip]
> +++ b/doc/device-tree-bindings/memory/memory.txt
> @@ -0,0 +1,67 @@
> +* Memory binding
> +
> +The memory binding for U-Boot is as in the ePAPR with the following additions:

I am wary of being different from ePAPR / Linux Kernel.  What do we need
this for / when do we use it?
Simon Glass Oct. 24, 2014, 8:04 p.m. UTC | #2
Hi Tom,

On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
> On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
>
>> From: Michael Pratt <mpratt@chromium.org>
>>
>> Support a default memory bank, specified in reg, as well as
>> board-specific memory banks in subtree board-id nodes.
>>
>> This allows memory information to be provided in the device tree,
>> rather than hard-coded in, which will make it simpler to handle
>> similar devices with different memory banks, as the board-id values
>> or masks can be used to match devices.
> [snip]
>> +++ b/doc/device-tree-bindings/memory/memory.txt
>> @@ -0,0 +1,67 @@
>> +* Memory binding
>> +
>> +The memory binding for U-Boot is as in the ePAPR with the following additions:
>
> I am wary of being different from ePAPR / Linux Kernel.  What do we need
> this for / when do we use it?

This extends the existing binding. It allows the location and size of
memory to be set by a board ID. Unfortunately on sopme hardware you
get a hang if you try to access memory that doesn't exist, so this
allows the range of available memory to be defined - or at least the
maximum bound since we still probe the memory size within that range.

This feature is used on several Exynos Chromebooks.

Regards,
Simon
Tom Rini Oct. 27, 2014, 2:24 p.m. UTC | #3
On Fri, Oct 24, 2014 at 02:04:00PM -0600, Simon Glass wrote:
> Hi Tom,
> 
> On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
> > On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
> >
> >> From: Michael Pratt <mpratt@chromium.org>
> >>
> >> Support a default memory bank, specified in reg, as well as
> >> board-specific memory banks in subtree board-id nodes.
> >>
> >> This allows memory information to be provided in the device tree,
> >> rather than hard-coded in, which will make it simpler to handle
> >> similar devices with different memory banks, as the board-id values
> >> or masks can be used to match devices.
> > [snip]
> >> +++ b/doc/device-tree-bindings/memory/memory.txt
> >> @@ -0,0 +1,67 @@
> >> +* Memory binding
> >> +
> >> +The memory binding for U-Boot is as in the ePAPR with the following additions:
> >
> > I am wary of being different from ePAPR / Linux Kernel.  What do we need
> > this for / when do we use it?
> 
> This extends the existing binding. It allows the location and size of
> memory to be set by a board ID. Unfortunately on sopme hardware you
> get a hang if you try to access memory that doesn't exist, so this
> allows the range of available memory to be defined - or at least the
> maximum bound since we still probe the memory size within that range.
> 
> This feature is used on several Exynos Chromebooks.

So that you can use the same DT on several disjoint boards?  How does
this work with the kernel, does U-Boot then pass along only the correct
map?  Patches to the kernel to also deal with this?
Simon Glass Oct. 27, 2014, 6:50 p.m. UTC | #4
Hi Tom,

On 27 October 2014 08:24, Tom Rini <trini@ti.com> wrote:
> On Fri, Oct 24, 2014 at 02:04:00PM -0600, Simon Glass wrote:
>> Hi Tom,
>>
>> On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
>> > On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
>> >
>> >> From: Michael Pratt <mpratt@chromium.org>
>> >>
>> >> Support a default memory bank, specified in reg, as well as
>> >> board-specific memory banks in subtree board-id nodes.
>> >>
>> >> This allows memory information to be provided in the device tree,
>> >> rather than hard-coded in, which will make it simpler to handle
>> >> similar devices with different memory banks, as the board-id values
>> >> or masks can be used to match devices.
>> > [snip]
>> >> +++ b/doc/device-tree-bindings/memory/memory.txt
>> >> @@ -0,0 +1,67 @@
>> >> +* Memory binding
>> >> +
>> >> +The memory binding for U-Boot is as in the ePAPR with the following additions:
>> >
>> > I am wary of being different from ePAPR / Linux Kernel.  What do we need
>> > this for / when do we use it?
>>
>> This extends the existing binding. It allows the location and size of
>> memory to be set by a board ID. Unfortunately on sopme hardware you
>> get a hang if you try to access memory that doesn't exist, so this
>> allows the range of available memory to be defined - or at least the
>> maximum bound since we still probe the memory size within that range.
>>
>> This feature is used on several Exynos Chromebooks.
>
> So that you can use the same DT on several disjoint boards?  How does
> this work with the kernel, does U-Boot then pass along only the correct
> map?  Patches to the kernel to also deal with this?

Typically a board may have variants with different amounts of memory,
detected at run-time by GPIOs. We want the option of using the same DT
for these, similar to what the Compulab people were talking about -
otherwise we have something of an explosion of combinations.

Yes U-Boot (already) puts the correct memory map together for the
kernel, so it all works from start to finish.

Regards,
Simon
Simon Glass Nov. 12, 2014, 3:13 p.m. UTC | #5
Hi,

On 27 October 2014 12:50, Simon Glass <sjg@chromium.org> wrote:
> Hi Tom,
>
> On 27 October 2014 08:24, Tom Rini <trini@ti.com> wrote:
>> On Fri, Oct 24, 2014 at 02:04:00PM -0600, Simon Glass wrote:
>>> Hi Tom,
>>>
>>> On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
>>> > On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
>>> >
>>> >> From: Michael Pratt <mpratt@chromium.org>
>>> >>
>>> >> Support a default memory bank, specified in reg, as well as
>>> >> board-specific memory banks in subtree board-id nodes.
>>> >>
>>> >> This allows memory information to be provided in the device tree,
>>> >> rather than hard-coded in, which will make it simpler to handle
>>> >> similar devices with different memory banks, as the board-id values
>>> >> or masks can be used to match devices.
>>> > [snip]
>>> >> +++ b/doc/device-tree-bindings/memory/memory.txt
>>> >> @@ -0,0 +1,67 @@
>>> >> +* Memory binding
>>> >> +
>>> >> +The memory binding for U-Boot is as in the ePAPR with the following additions:
>>> >
>>> > I am wary of being different from ePAPR / Linux Kernel.  What do we need
>>> > this for / when do we use it?
>>>
>>> This extends the existing binding. It allows the location and size of
>>> memory to be set by a board ID. Unfortunately on sopme hardware you
>>> get a hang if you try to access memory that doesn't exist, so this
>>> allows the range of available memory to be defined - or at least the
>>> maximum bound since we still probe the memory size within that range.
>>>
>>> This feature is used on several Exynos Chromebooks.
>>
>> So that you can use the same DT on several disjoint boards?  How does
>> this work with the kernel, does U-Boot then pass along only the correct
>> map?  Patches to the kernel to also deal with this?
>
> Typically a board may have variants with different amounts of memory,
> detected at run-time by GPIOs. We want the option of using the same DT
> for these, similar to what the Compulab people were talking about -
> otherwise we have something of an explosion of combinations.
>
> Yes U-Boot (already) puts the correct memory map together for the
> kernel, so it all works from start to finish.

Are there any more thoughts on this one? I'd like to pull this series
into the u-boot-fdt tree.

Regards,
Simon
Tom Rini Nov. 12, 2014, 9:42 p.m. UTC | #6
On Mon, Oct 27, 2014 at 12:50:39PM -0600, Simon Glass wrote:
> Hi Tom,
> 
> On 27 October 2014 08:24, Tom Rini <trini@ti.com> wrote:
> > On Fri, Oct 24, 2014 at 02:04:00PM -0600, Simon Glass wrote:
> >> Hi Tom,
> >>
> >> On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
> >> > On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
> >> >
> >> >> From: Michael Pratt <mpratt@chromium.org>
> >> >>
> >> >> Support a default memory bank, specified in reg, as well as
> >> >> board-specific memory banks in subtree board-id nodes.
> >> >>
> >> >> This allows memory information to be provided in the device tree,
> >> >> rather than hard-coded in, which will make it simpler to handle
> >> >> similar devices with different memory banks, as the board-id values
> >> >> or masks can be used to match devices.
> >> > [snip]
> >> >> +++ b/doc/device-tree-bindings/memory/memory.txt
> >> >> @@ -0,0 +1,67 @@
> >> >> +* Memory binding
> >> >> +
> >> >> +The memory binding for U-Boot is as in the ePAPR with the following additions:
> >> >
> >> > I am wary of being different from ePAPR / Linux Kernel.  What do we need
> >> > this for / when do we use it?
> >>
> >> This extends the existing binding. It allows the location and size of
> >> memory to be set by a board ID. Unfortunately on sopme hardware you
> >> get a hang if you try to access memory that doesn't exist, so this
> >> allows the range of available memory to be defined - or at least the
> >> maximum bound since we still probe the memory size within that range.
> >>
> >> This feature is used on several Exynos Chromebooks.
> >
> > So that you can use the same DT on several disjoint boards?  How does
> > this work with the kernel, does U-Boot then pass along only the correct
> > map?  Patches to the kernel to also deal with this?
> 
> Typically a board may have variants with different amounts of memory,
> detected at run-time by GPIOs. We want the option of using the same DT
> for these, similar to what the Compulab people were talking about -
> otherwise we have something of an explosion of combinations.
> 
> Yes U-Boot (already) puts the correct memory map together for the
> kernel, so it all works from start to finish.

I'm still not super happy here.  If you have different boards then you
provide different device trees and the binary that can deal with board A
or B, based on what the DT says.

If you can't just use get_ram_size, can't you use one of the other
existing hooks such as ft_board_setup (which runs after arch_fixup_fdt)
to set the memory size?  On PowerPC boards for example this is where the
memory node is set anyhow, not arch_fixup_fdt (which is one of those ARM
vs PowerPC/everyone-else things I dislike).
Simon Glass Nov. 12, 2014, 10:14 p.m. UTC | #7
Hi Tom,


On 12 November 2014 14:42, Tom Rini <trini@ti.com> wrote:
> On Mon, Oct 27, 2014 at 12:50:39PM -0600, Simon Glass wrote:
>> Hi Tom,
>>
>> On 27 October 2014 08:24, Tom Rini <trini@ti.com> wrote:
>> > On Fri, Oct 24, 2014 at 02:04:00PM -0600, Simon Glass wrote:
>> >> Hi Tom,
>> >>
>> >> On 24 October 2014 12:49, Tom Rini <trini@ti.com> wrote:
>> >> > On Thu, Oct 23, 2014 at 06:58:50PM -0600, Simon Glass wrote:
>> >> >
>> >> >> From: Michael Pratt <mpratt@chromium.org>
>> >> >>
>> >> >> Support a default memory bank, specified in reg, as well as
>> >> >> board-specific memory banks in subtree board-id nodes.
>> >> >>
>> >> >> This allows memory information to be provided in the device tree,
>> >> >> rather than hard-coded in, which will make it simpler to handle
>> >> >> similar devices with different memory banks, as the board-id values
>> >> >> or masks can be used to match devices.
>> >> > [snip]
>> >> >> +++ b/doc/device-tree-bindings/memory/memory.txt
>> >> >> @@ -0,0 +1,67 @@
>> >> >> +* Memory binding
>> >> >> +
>> >> >> +The memory binding for U-Boot is as in the ePAPR with the following additions:
>> >> >
>> >> > I am wary of being different from ePAPR / Linux Kernel.  What do we need
>> >> > this for / when do we use it?
>> >>
>> >> This extends the existing binding. It allows the location and size of
>> >> memory to be set by a board ID. Unfortunately on sopme hardware you
>> >> get a hang if you try to access memory that doesn't exist, so this
>> >> allows the range of available memory to be defined - or at least the
>> >> maximum bound since we still probe the memory size within that range.
>> >>
>> >> This feature is used on several Exynos Chromebooks.
>> >
>> > So that you can use the same DT on several disjoint boards?  How does
>> > this work with the kernel, does U-Boot then pass along only the correct
>> > map?  Patches to the kernel to also deal with this?
>>
>> Typically a board may have variants with different amounts of memory,
>> detected at run-time by GPIOs. We want the option of using the same DT
>> for these, similar to what the Compulab people were talking about -
>> otherwise we have something of an explosion of combinations.
>>
>> Yes U-Boot (already) puts the correct memory map together for the
>> kernel, so it all works from start to finish.
>
> I'm still not super happy here.  If you have different boards then you
> provide different device trees and the binary that can deal with board A
> or B, based on what the DT says.

(Note: I'm talking about the U-Boot device tree, and indeed for the
kernel we will have two different device trees)

That assumes that you can put a different binary on two different
boards. The same discussion came up with the Compulab series, where
they have different features but what to use the same U-Boot binary in
production for them, since it is such a pain to deal with different
incompatible binaries for board variants. It gets worse when you have
a module and want to support different base boards.

>
> If you can't just use get_ram_size, can't you use one of the other
> existing hooks such as ft_board_setup (which runs after arch_fixup_fdt)
> to set the memory size?  On PowerPC boards for example this is where the
> memory node is set anyhow, not arch_fixup_fdt (which is one of those ARM
> vs PowerPC/everyone-else things I dislike).

I think here you are talking about setting up the device tree for the
kernel. Yes, U-Boot correctly sets up the memory for the kernel, by
copying information from the SDRAM bank list into the kernel's device
tree. The question is of course, how do we get the correct SDRAM bank
list in the first place.

What I am talking about here is the memory for U-Boot - i.e. how does
U-Boot know how much memory is available. The scheme here is that you
can have a board ID which controls this, with the U-Boot device tree
allowing the options to be specified (i.e. the mapping from board ID
to SDRAM banks)

Regards,
Simon
Tom Rini Nov. 20, 2014, 2:37 p.m. UTC | #8
On Wed, Nov 12, 2014 at 03:14:35PM -0700, Simon Glass wrote:

[snip, and sorry for the late reply]
> What I am talking about here is the memory for U-Boot - i.e. how does
> U-Boot know how much memory is available. The scheme here is that you
> can have a board ID which controls this, with the U-Boot device tree
> allowing the options to be specified (i.e. the mapping from board ID
> to SDRAM banks)

OK.  So what I'm getting at is how is this different from other boards
where we say "Oh, this is $FOO, it has $X LPDRR2 RAM" and "Oh, this is
$BAR, it has $Y DDR3 RAM" ?  The only thing I'm able to think of is that
we cannot call get_ram_size() to see the _size_ of the RAM after doing
$whatever to see what board we're on and doing the right configuration
for the DDR controller or whatever.
Simon Glass Nov. 20, 2014, 5:42 p.m. UTC | #9
Hi Tom,

On 20 November 2014 14:37, Tom Rini <trini@ti.com> wrote:
> On Wed, Nov 12, 2014 at 03:14:35PM -0700, Simon Glass wrote:
>
> [snip, and sorry for the late reply]
>> What I am talking about here is the memory for U-Boot - i.e. how does
>> U-Boot know how much memory is available. The scheme here is that you
>> can have a board ID which controls this, with the U-Boot device tree
>> allowing the options to be specified (i.e. the mapping from board ID
>> to SDRAM banks)
>
> OK.  So what I'm getting at is how is this different from other boards
> where we say "Oh, this is $FOO, it has $X LPDRR2 RAM" and "Oh, this is
> $BAR, it has $Y DDR3 RAM" ?  The only thing I'm able to think of is that
> we cannot call get_ram_size() to see the _size_ of the RAM after doing
> $whatever to see what board we're on and doing the right configuration
> for the DDR controller or whatever.

Yes the memory type can go in the FDT too (and does for snow and pit).

That's right, we can call get_ram_size() but only within the known
boundaries. If we try to probe a 2GB machine as if it has 4GB of RAM
it will hang. So we have to *know*.

Regards,
Simon
Simon Glass Dec. 10, 2014, 5:31 a.m. UTC | #10
Hi Tom,

On 20 November 2014 at 10:42, Simon Glass <sjg@chromium.org> wrote:
> Hi Tom,
>
> On 20 November 2014 14:37, Tom Rini <trini@ti.com> wrote:
>> On Wed, Nov 12, 2014 at 03:14:35PM -0700, Simon Glass wrote:
>>
>> [snip, and sorry for the late reply]
>>> What I am talking about here is the memory for U-Boot - i.e. how does
>>> U-Boot know how much memory is available. The scheme here is that you
>>> can have a board ID which controls this, with the U-Boot device tree
>>> allowing the options to be specified (i.e. the mapping from board ID
>>> to SDRAM banks)
>>
>> OK.  So what I'm getting at is how is this different from other boards
>> where we say "Oh, this is $FOO, it has $X LPDRR2 RAM" and "Oh, this is
>> $BAR, it has $Y DDR3 RAM" ?  The only thing I'm able to think of is that
>> we cannot call get_ram_size() to see the _size_ of the RAM after doing
>> $whatever to see what board we're on and doing the right configuration
>> for the DDR controller or whatever.
>
> Yes the memory type can go in the FDT too (and does for snow and pit).
>
> That's right, we can call get_ram_size() but only within the known
> boundaries. If we try to probe a 2GB machine as if it has 4GB of RAM
> it will hang. So we have to *know*.

I have left this one out so far, but would like to apply it. With this
I can support Pit and Pi (and with a bit more work Snow and Spring)
with the same U-Boot board, dealing with the memory differences
between the boards.

Regards,
Simon
diff mbox

Patch

diff --git a/common/fdt_support.c b/common/fdt_support.c
index 46a15e7..6da5297 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -382,7 +382,7 @@  void do_fixup_by_compat_u32(void *fdt, const char *compat,
 /*
  * fdt_pack_reg - pack address and size array into the "reg"-suitable stream
  */
-static int fdt_pack_reg(const void *fdt, void *buf, uint64_t *address,
+int fdt_pack_reg(const void *fdt, void *buf, uint64_t *address,
 			uint64_t *size, int n)
 {
 	int i;
diff --git a/doc/device-tree-bindings/memory/memory.txt b/doc/device-tree-bindings/memory/memory.txt
new file mode 100644
index 0000000..321894e
--- /dev/null
+++ b/doc/device-tree-bindings/memory/memory.txt
@@ -0,0 +1,67 @@ 
+* Memory binding
+
+The memory binding for U-Boot is as in the ePAPR with the following additions:
+
+Optional subnodes can be used defining the memory layout for different board
+ID masks. To match a set of board ids, a board-id node may define match-mask
+and match-value ints to define a mask to apply to the board id, and the value
+that the result should have for the match to be considered valid. The mask
+defaults to -1, meaning that the value must fully match the board id.
+
+If subnodes are present, then the /memory node must define these properties:
+
+- #address-cells: should be 1.
+- #size-cells: should be 0.
+
+Each subnode must define
+
+ reg - board ID or mask for this subnode
+ memory-banks - list of memory banks in the same format as normal
+
+Each subnode may optionally define:
+
+ match-mask - A mask to apply to the board id.  This must be accompanied by
+              match-value.
+ match-value - The required resulting value of the board id mask for the given
+	       node to be considered a match.
+ auto-size - Indicates that the value given for a bank is the maximum size,
+	     each bank is probed to determine its actual size, which may be
+	     smaller
+
+
+The board id determination is up to the vendor and is not defined by this
+binding.
+
+Example:
+
+memory {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0x20000000 0x20000000
+		0x40000000 0x20000000
+		0x60000000 0x20000000
+		0x80000000 0x20000000>;
+	auto-size;
+	board-id@0 {
+		match-value = <17>;
+		reg = <0x20000000 0x20000000
+			0x40000000 0x20000000>;
+	};
+	board-id@1 {
+		match-mask = <2>;
+		match-value = <2>;
+		reg = <0x20000000 0x20000000
+			0x40000000 0x20000000
+			0x60000000 0x20000000
+			0x80000000 0x20000000
+			0xa0000000 0x20000000
+			0xc0000000 0x20000000
+			0xe0000000 0x20000000>;
+	};
+};
+
+
+This shows a system with the following properties:
+* Default of 2GB of memory, auto-sized, so could be smaller
+* 3.5GB of memory (with no auto-size) if (board id & 2) is 2
+* 1GB of memory (with no auto-size) if board id is 17.
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 4ae77be..5fb86b0 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -661,4 +661,37 @@  int fdt_get_named_resource(const void *fdt, int node, const char *property,
  */
 int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf);
 
+/**
+ * Decode the size of memory
+ *
+ * RAM size is normally set in a /memory node and consists of a list of
+ * (base, size) cells in the 'reg' property. This information is used to
+ * determine the total available memory as well as the address and size
+ * of each bank.
+ *
+ * Optionally the memory configuration can vary depending on a board id,
+ * typically read from strapping resistors or an EEPROM on the board.
+ *
+ * Finally, memory size can be detected (within certain limits) by probing
+ * the available memory. It is safe to do so within the limits provides by
+ * the board's device tree information. This makes it possible to produce
+ * boards with different memory sizes, where the device tree specifies the
+ * maximum memory configuration, and the smaller memory configuration is
+ * probed.
+ *
+ * This function decodes that information, returning the memory base address,
+ * size and bank information. See the memory.txt binding for full
+ * documentation.
+ *
+ * @param blob		Device tree blob
+ * @param area		Name of node to check (NULL means "/memory")
+ * @param board_id	Board ID to look up
+ * @param basep		Returns base address of first memory bank (NULL to
+ *			ignore)
+ * @param sizep		Returns total memory size (NULL to ignore)
+ * @param bd		Updated with the memory bank information (NULL to skip)
+ * @return 0 if OK, -ve on error
+ */
+int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
+			   phys_addr_t *basep, phys_size_t *sizep, bd_t *bd);
 #endif
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 9714620..d5a6a3e 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -6,10 +6,12 @@ 
 #ifndef USE_HOSTCC
 #include <common.h>
 #include <errno.h>
+#include <fdt_support.h>
 #include <serial.h>
 #include <libfdt.h>
 #include <fdtdec.h>
 #include <linux/ctype.h>
+#include <asm/types.h>
 
 #include <asm/gpio.h>
 
@@ -780,4 +782,109 @@  int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf)
 
 	return 0;
 }
+
+int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
+			   phys_addr_t *basep, phys_size_t *sizep, bd_t *bd)
+{
+	int addr_cells, size_cells;
+	const u32 *cell, *end;
+	u64 total_size, size, addr;
+	int node, child;
+	bool auto_size;
+	int bank;
+	int len;
+
+	debug("%s: board_id=%d\n", __func__, board_id);
+	if (!area)
+		area = "/memory";
+	node = fdt_path_offset(blob, area);
+	if (node < 0) {
+		debug("No %s node found\n", area);
+		return -ENOENT;
+	}
+
+	cell = fdt_getprop(blob, node, "reg", &len);
+	if (!cell) {
+		debug("No reg property found\n");
+		return -ENOENT;
+	}
+
+	addr_cells = fdt_address_cells(blob, node);
+	size_cells = fdt_size_cells(blob, node);
+
+	/* Check the board id and mask */
+	for (child = fdt_first_subnode(blob, node);
+	     child >= 0;
+	     child = fdt_next_subnode(blob, child)) {
+		int match_mask, match_value;
+
+		match_mask = fdtdec_get_int(blob, child, "match-mask", -1);
+		match_value = fdtdec_get_int(blob, child, "match-value", -1);
+
+		if (match_value >= 0 &&
+		    ((board_id & match_mask) == match_value)) {
+			/* Found matching mask */
+			debug("Found matching mask %d\n", match_mask);
+			node = child;
+			cell = fdt_getprop(blob, node, "reg", &len);
+			if (!cell) {
+				debug("No memory-banks property found\n");
+				return -EINVAL;
+			}
+			break;
+		}
+	}
+	/* Note: if no matching subnode was found we use the parent node */
+
+	if (bd) {
+		memset(bd->bi_dram, '\0', sizeof(bd->bi_dram[0]) *
+						CONFIG_NR_DRAM_BANKS);
+	}
+
+	auto_size = fdtdec_get_bool(blob, node, "auto-size");
+
+	total_size = 0;
+	end = cell + len / 4 - addr_cells - size_cells;
+	debug("cell at %p, end %p\n", cell, end);
+	for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+		if (cell > end)
+			break;
+		addr = 0;
+		if (addr_cells == 2)
+			addr += (u64)fdt32_to_cpu(*cell++) << 32UL;
+		addr += fdt32_to_cpu(*cell++);
+		if (bd)
+			bd->bi_dram[bank].start = addr;
+		if (basep && !bank)
+			*basep = (phys_addr_t)addr;
+
+		size = 0;
+		if (size_cells == 2)
+			size += (u64)fdt32_to_cpu(*cell++) << 32UL;
+		size += fdt32_to_cpu(*cell++);
+
+		if (auto_size) {
+			u64 new_size;
+
+			debug("Auto-sizing %llx, size %llx: ", addr, size);
+			new_size = get_ram_size((long *)(uintptr_t)addr, size);
+			if (new_size == size) {
+				debug("OK\n");
+			} else {
+				debug("sized to %llx\n", new_size);
+				size = new_size;
+			}
+		}
+
+		if (bd)
+			bd->bi_dram[bank].size = size;
+		total_size += size;
+	}
+
+	debug("Memory size %llu\n", total_size);
+	if (sizep)
+		*sizep = (phys_size_t)total_size;
+
+	return 0;
+}
 #endif