mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
arm: a37xx: pci: Fix a3700_fdt_fix_pcie_regions() function
Current version of this function uses a lot of incorrect assumptions about
the `ranges` DT property:
* parent(#address-cells) == 2
* #size-cells == 2
* number of entries == 2
* address size of first entry == 0x1000000
* second child address entry == base + 0x1000000
Trying to increase PCIe MEM space to more than 16 MiB leads to an overlap
with PCIe IO space, and trying to define additional MEM space (as a third
entry in the `ranges` DT property) causes U-Boot to crash when booting the
kernel.
## Flattened Device Tree blob at 04f00000
Booting using the fdt blob at 0x4f00000
Loading Device Tree to 000000001fb01000, end 000000001fb08f12 ... OK
ERROR: board-specific fdt fixup failed: <unknown error>
- must RESET the board to recover.
Fix a3700_fdt_fix_pcie_regions() to properly parse and update all addresses
in the `ranges` property according to
https://elinux.org/Device_Tree_Usage#PCI_Address_Translation
Now it is possible to increase PCIe MEM space from 16 MiB to maximal value
of 127 MiB.
Signed-off-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
Fixes: cb2ddb291e
("arm64: mvebu: a37xx: add device-tree fixer for PCIe regions")
Reviewed-by: Stefan Roese <sr@denx.de>
This commit is contained in:
parent
46b679e3ec
commit
4a82fca8e3
1 changed files with 60 additions and 14 deletions
|
@ -8,6 +8,7 @@
|
||||||
#include <cpu_func.h>
|
#include <cpu_func.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <fdtdec.h>
|
#include <fdtdec.h>
|
||||||
|
#include <fdt_support.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <asm/global_data.h>
|
#include <asm/global_data.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
@ -280,36 +281,81 @@ static u32 find_pcie_window_base(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fdt_setprop_inplace_u32_partial(void *blob, int node,
|
||||||
|
const char *name,
|
||||||
|
u32 idx, u32 val)
|
||||||
|
{
|
||||||
|
val = cpu_to_fdt32(val);
|
||||||
|
|
||||||
|
return fdt_setprop_inplace_namelen_partial(blob, node, name,
|
||||||
|
strlen(name),
|
||||||
|
idx * sizeof(u32),
|
||||||
|
&val, sizeof(u32));
|
||||||
|
}
|
||||||
|
|
||||||
int a3700_fdt_fix_pcie_regions(void *blob)
|
int a3700_fdt_fix_pcie_regions(void *blob)
|
||||||
{
|
{
|
||||||
u32 new_ranges[14], base;
|
int acells, pacells, scells;
|
||||||
|
u32 base, fix_offset;
|
||||||
const u32 *ranges;
|
const u32 *ranges;
|
||||||
int node, len;
|
int node, pnode;
|
||||||
|
int ret, i, len;
|
||||||
|
|
||||||
|
base = find_pcie_window_base();
|
||||||
|
if (base == -1)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
node = fdt_node_offset_by_compatible(blob, -1, "marvell,armada-3700-pcie");
|
node = fdt_node_offset_by_compatible(blob, -1, "marvell,armada-3700-pcie");
|
||||||
if (node < 0)
|
if (node < 0)
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
ranges = fdt_getprop(blob, node, "ranges", &len);
|
ranges = fdt_getprop(blob, node, "ranges", &len);
|
||||||
if (!ranges)
|
if (!ranges || len % sizeof(u32))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
if (len != sizeof(new_ranges))
|
/*
|
||||||
return -EINVAL;
|
* The "ranges" property is an array of
|
||||||
|
* { <child address> <parent address> <size in child address space> }
|
||||||
|
*
|
||||||
|
* All 3 elements can span a diffent number of cells. Fetch their sizes.
|
||||||
|
*/
|
||||||
|
pnode = fdt_parent_offset(blob, node);
|
||||||
|
acells = fdt_address_cells(blob, node);
|
||||||
|
pacells = fdt_address_cells(blob, pnode);
|
||||||
|
scells = fdt_size_cells(blob, node);
|
||||||
|
|
||||||
memcpy(new_ranges, ranges, len);
|
/* Child PCI addresses always use 3 cells */
|
||||||
|
if (acells != 3)
|
||||||
base = find_pcie_window_base();
|
|
||||||
if (base == -1)
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
new_ranges[2] = cpu_to_fdt32(base);
|
/* Calculate fixup offset from first child address (in last cell) */
|
||||||
new_ranges[4] = new_ranges[2];
|
fix_offset = base - fdt32_to_cpu(ranges[2]);
|
||||||
|
|
||||||
new_ranges[9] = cpu_to_fdt32(base + 0x1000000);
|
/*
|
||||||
new_ranges[11] = new_ranges[9];
|
* Fix address (last cell) of each child address and each parent
|
||||||
|
* address
|
||||||
|
*/
|
||||||
|
for (i = 0; i < len / sizeof(u32); i += acells + pacells + scells) {
|
||||||
|
int idx;
|
||||||
|
|
||||||
return fdt_setprop_inplace(blob, node, "ranges", new_ranges, len);
|
/* fix child address */
|
||||||
|
idx = i + acells - 1;
|
||||||
|
ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx,
|
||||||
|
fdt32_to_cpu(ranges[idx]) +
|
||||||
|
fix_offset);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* fix parent address */
|
||||||
|
idx = i + acells + pacells - 1;
|
||||||
|
ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx,
|
||||||
|
fdt32_to_cpu(ranges[idx]) +
|
||||||
|
fix_offset);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_cpu(void)
|
void reset_cpu(void)
|
||||||
|
|
Loading…
Reference in a new issue