162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * FDT Address translation based on u-boot fdt_support.c which in turn was
462306a36Sopenharmony_ci * based on the kernel unflattened DT address translation code.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 2007
762306a36Sopenharmony_ci * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright 2010-2011 Freescale Semiconductor, Inc.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt)	"OF: fdt: " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/libfdt.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_fdt.h>
1862306a36Sopenharmony_ci#include <linux/sizes.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Max address size we deal with */
2162306a36Sopenharmony_ci#define OF_MAX_ADDR_CELLS	4
2262306a36Sopenharmony_ci#define OF_CHECK_COUNTS(na, ns)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
2362306a36Sopenharmony_ci			(ns) > 0)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Debug utility */
2662306a36Sopenharmony_ci#ifdef DEBUG
2762306a36Sopenharmony_cistatic void __init of_dump_addr(const char *s, const __be32 *addr, int na)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	pr_debug("%s", s);
3062306a36Sopenharmony_ci	while(na--)
3162306a36Sopenharmony_ci		pr_cont(" %08x", *(addr++));
3262306a36Sopenharmony_ci	pr_cont("\n");
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci#else
3562306a36Sopenharmony_cistatic void __init of_dump_addr(const char *s, const __be32 *addr, int na) { }
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Callbacks for bus specific translators */
3962306a36Sopenharmony_cistruct of_bus {
4062306a36Sopenharmony_ci	void		(*count_cells)(const void *blob, int parentoffset,
4162306a36Sopenharmony_ci				int *addrc, int *sizec);
4262306a36Sopenharmony_ci	u64		(*map)(__be32 *addr, const __be32 *range,
4362306a36Sopenharmony_ci				int na, int ns, int pna);
4462306a36Sopenharmony_ci	int		(*translate)(__be32 *addr, u64 offset, int na);
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Default translator (generic bus) */
4862306a36Sopenharmony_cistatic void __init fdt_bus_default_count_cells(const void *blob, int parentoffset,
4962306a36Sopenharmony_ci					       int *addrc, int *sizec)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	const __be32 *prop;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (addrc) {
5462306a36Sopenharmony_ci		prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
5562306a36Sopenharmony_ci		if (prop)
5662306a36Sopenharmony_ci			*addrc = be32_to_cpup(prop);
5762306a36Sopenharmony_ci		else
5862306a36Sopenharmony_ci			*addrc = dt_root_addr_cells;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (sizec) {
6262306a36Sopenharmony_ci		prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
6362306a36Sopenharmony_ci		if (prop)
6462306a36Sopenharmony_ci			*sizec = be32_to_cpup(prop);
6562306a36Sopenharmony_ci		else
6662306a36Sopenharmony_ci			*sizec = dt_root_size_cells;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range,
7162306a36Sopenharmony_ci				      int na, int ns, int pna)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u64 cp, s, da;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	cp = of_read_number(range, na);
7662306a36Sopenharmony_ci	s  = of_read_number(range + na + pna, ns);
7762306a36Sopenharmony_ci	da = of_read_number(addr, na);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	pr_debug("default map, cp=%llx, s=%llx, da=%llx\n",
8062306a36Sopenharmony_ci	    cp, s, da);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (da < cp || da >= (cp + s))
8362306a36Sopenharmony_ci		return OF_BAD_ADDR;
8462306a36Sopenharmony_ci	return da - cp;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	u64 a = of_read_number(addr, na);
9062306a36Sopenharmony_ci	memset(addr, 0, na * 4);
9162306a36Sopenharmony_ci	a += offset;
9262306a36Sopenharmony_ci	if (na > 1)
9362306a36Sopenharmony_ci		addr[na - 2] = cpu_to_fdt32(a >> 32);
9462306a36Sopenharmony_ci	addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* Array of bus specific translators */
10062306a36Sopenharmony_cistatic const struct of_bus of_busses[] __initconst = {
10162306a36Sopenharmony_ci	/* Default */
10262306a36Sopenharmony_ci	{
10362306a36Sopenharmony_ci		.count_cells = fdt_bus_default_count_cells,
10462306a36Sopenharmony_ci		.map = fdt_bus_default_map,
10562306a36Sopenharmony_ci		.translate = fdt_bus_default_translate,
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int __init fdt_translate_one(const void *blob, int parent,
11062306a36Sopenharmony_ci				    const struct of_bus *bus,
11162306a36Sopenharmony_ci				    const struct of_bus *pbus, __be32 *addr,
11262306a36Sopenharmony_ci				    int na, int ns, int pna, const char *rprop)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	const __be32 *ranges;
11562306a36Sopenharmony_ci	int rlen;
11662306a36Sopenharmony_ci	int rone;
11762306a36Sopenharmony_ci	u64 offset = OF_BAD_ADDR;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ranges = fdt_getprop(blob, parent, rprop, &rlen);
12062306a36Sopenharmony_ci	if (!ranges)
12162306a36Sopenharmony_ci		return 1;
12262306a36Sopenharmony_ci	if (rlen == 0) {
12362306a36Sopenharmony_ci		offset = of_read_number(addr, na);
12462306a36Sopenharmony_ci		memset(addr, 0, pna * 4);
12562306a36Sopenharmony_ci		pr_debug("empty ranges, 1:1 translation\n");
12662306a36Sopenharmony_ci		goto finish;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	pr_debug("walking ranges...\n");
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Now walk through the ranges */
13262306a36Sopenharmony_ci	rlen /= 4;
13362306a36Sopenharmony_ci	rone = na + pna + ns;
13462306a36Sopenharmony_ci	for (; rlen >= rone; rlen -= rone, ranges += rone) {
13562306a36Sopenharmony_ci		offset = bus->map(addr, ranges, na, ns, pna);
13662306a36Sopenharmony_ci		if (offset != OF_BAD_ADDR)
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	if (offset == OF_BAD_ADDR) {
14062306a36Sopenharmony_ci		pr_debug("not found !\n");
14162306a36Sopenharmony_ci		return 1;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	memcpy(addr, ranges + na, 4 * pna);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci finish:
14662306a36Sopenharmony_ci	of_dump_addr("parent translation for:", addr, pna);
14762306a36Sopenharmony_ci	pr_debug("with offset: %llx\n", offset);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Translate it into parent bus space */
15062306a36Sopenharmony_ci	return pbus->translate(addr, offset, pna);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*
15462306a36Sopenharmony_ci * Translate an address from the device-tree into a CPU physical address,
15562306a36Sopenharmony_ci * this walks up the tree and applies the various bus mappings on the
15662306a36Sopenharmony_ci * way.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * Note: We consider that crossing any level with #size-cells == 0 to mean
15962306a36Sopenharmony_ci * that translation is impossible (that is we are not dealing with a value
16062306a36Sopenharmony_ci * that can be mapped to a cpu physical address). This is not really specified
16162306a36Sopenharmony_ci * that way, but this is traditionally the way IBM at least do things
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic u64 __init fdt_translate_address(const void *blob, int node_offset)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int parent, len;
16662306a36Sopenharmony_ci	const struct of_bus *bus, *pbus;
16762306a36Sopenharmony_ci	const __be32 *reg;
16862306a36Sopenharmony_ci	__be32 addr[OF_MAX_ADDR_CELLS];
16962306a36Sopenharmony_ci	int na, ns, pna, pns;
17062306a36Sopenharmony_ci	u64 result = OF_BAD_ADDR;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	pr_debug("** translation for device %s **\n",
17362306a36Sopenharmony_ci		 fdt_get_name(blob, node_offset, NULL));
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	reg = fdt_getprop(blob, node_offset, "reg", &len);
17662306a36Sopenharmony_ci	if (!reg) {
17762306a36Sopenharmony_ci		pr_err("warning: device tree node '%s' has no address.\n",
17862306a36Sopenharmony_ci			fdt_get_name(blob, node_offset, NULL));
17962306a36Sopenharmony_ci		goto bail;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Get parent & match bus type */
18362306a36Sopenharmony_ci	parent = fdt_parent_offset(blob, node_offset);
18462306a36Sopenharmony_ci	if (parent < 0)
18562306a36Sopenharmony_ci		goto bail;
18662306a36Sopenharmony_ci	bus = &of_busses[0];
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Cound address cells & copy address locally */
18962306a36Sopenharmony_ci	bus->count_cells(blob, parent, &na, &ns);
19062306a36Sopenharmony_ci	if (!OF_CHECK_COUNTS(na, ns)) {
19162306a36Sopenharmony_ci		pr_err("Bad cell count for %s\n",
19262306a36Sopenharmony_ci		       fdt_get_name(blob, node_offset, NULL));
19362306a36Sopenharmony_ci		goto bail;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	memcpy(addr, reg, na * 4);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	pr_debug("bus (na=%d, ns=%d) on %s\n",
19862306a36Sopenharmony_ci		 na, ns, fdt_get_name(blob, parent, NULL));
19962306a36Sopenharmony_ci	of_dump_addr("translating address:", addr, na);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Translate */
20262306a36Sopenharmony_ci	for (;;) {
20362306a36Sopenharmony_ci		/* Switch to parent bus */
20462306a36Sopenharmony_ci		node_offset = parent;
20562306a36Sopenharmony_ci		parent = fdt_parent_offset(blob, node_offset);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/* If root, we have finished */
20862306a36Sopenharmony_ci		if (parent < 0) {
20962306a36Sopenharmony_ci			pr_debug("reached root node\n");
21062306a36Sopenharmony_ci			result = of_read_number(addr, na);
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		/* Get new parent bus and counts */
21562306a36Sopenharmony_ci		pbus = &of_busses[0];
21662306a36Sopenharmony_ci		pbus->count_cells(blob, parent, &pna, &pns);
21762306a36Sopenharmony_ci		if (!OF_CHECK_COUNTS(pna, pns)) {
21862306a36Sopenharmony_ci			pr_err("Bad cell count for %s\n",
21962306a36Sopenharmony_ci				fdt_get_name(blob, node_offset, NULL));
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		pr_debug("parent bus (na=%d, ns=%d) on %s\n",
22462306a36Sopenharmony_ci			 pna, pns, fdt_get_name(blob, parent, NULL));
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		/* Apply bus translation */
22762306a36Sopenharmony_ci		if (fdt_translate_one(blob, node_offset, bus, pbus,
22862306a36Sopenharmony_ci					addr, na, ns, pna, "ranges"))
22962306a36Sopenharmony_ci			break;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/* Complete the move up one level */
23262306a36Sopenharmony_ci		na = pna;
23362306a36Sopenharmony_ci		ns = pns;
23462306a36Sopenharmony_ci		bus = pbus;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		of_dump_addr("one level translation:", addr, na);
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci bail:
23962306a36Sopenharmony_ci	return result;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/**
24362306a36Sopenharmony_ci * of_flat_dt_translate_address - translate DT addr into CPU phys addr
24462306a36Sopenharmony_ci * @node: node in the flat blob
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_ciu64 __init of_flat_dt_translate_address(unsigned long node)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	return fdt_translate_address(initial_boot_params, node);
24962306a36Sopenharmony_ci}
250