• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * FDT Address translation based on u-boot fdt_support.c which in turn was
3  * based on the kernel unflattened DT address translation code.
4  *
5  * (C) Copyright 2007
6  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
7  *
8  * Copyright 2010-2011 Freescale Semiconductor, Inc.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2, or (at your option)
13  * any later version.
14  */
15 #include <linux/kernel.h>
16 #include <linux/libfdt.h>
17 #include <linux/of.h>
18 #include <linux/of_fdt.h>
19 #include <linux/sizes.h>
20 
21 /* Max address size we deal with */
22 #define OF_MAX_ADDR_CELLS	4
23 #define OF_CHECK_COUNTS(na, ns)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
24 			(ns) > 0)
25 
26 /* Debug utility */
27 #ifdef DEBUG
of_dump_addr(const char * s,const __be32 * addr,int na)28 static void __init of_dump_addr(const char *s, const __be32 *addr, int na)
29 {
30 	pr_debug("%s", s);
31 	while(na--)
32 		pr_cont(" %08x", *(addr++));
33 	pr_debug("\n");
34 }
35 #else
of_dump_addr(const char * s,const __be32 * addr,int na)36 static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { }
37 #endif
38 
39 /* Callbacks for bus specific translators */
40 struct of_bus {
41 	void		(*count_cells)(const void *blob, int parentoffset,
42 				int *addrc, int *sizec);
43 	u64		(*map)(__be32 *addr, const __be32 *range,
44 				int na, int ns, int pna);
45 	int		(*translate)(__be32 *addr, u64 offset, int na);
46 };
47 
48 /* Default translator (generic bus) */
fdt_bus_default_count_cells(const void * blob,int parentoffset,int * addrc,int * sizec)49 static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset,
50 					       int *addrc, int *sizec)
51 {
52 	const __be32 *prop;
53 
54 	if (addrc) {
55 		prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
56 		if (prop)
57 			*addrc = be32_to_cpup(prop);
58 		else
59 			*addrc = dt_root_addr_cells;
60 	}
61 
62 	if (sizec) {
63 		prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
64 		if (prop)
65 			*sizec = be32_to_cpup(prop);
66 		else
67 			*sizec = dt_root_size_cells;
68 	}
69 }
70 
fdt_bus_default_map(__be32 * addr,const __be32 * range,int na,int ns,int pna)71 static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range,
72 				      int na, int ns, int pna)
73 {
74 	u64 cp, s, da;
75 
76 	cp = of_read_number(range, na);
77 	s  = of_read_number(range + na + pna, ns);
78 	da = of_read_number(addr, na);
79 
80 	pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n",
81 	    cp, s, da);
82 
83 	if (da < cp || da >= (cp + s))
84 		return OF_BAD_ADDR;
85 	return da - cp;
86 }
87 
fdt_bus_default_translate(__be32 * addr,u64 offset,int na)88 static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na)
89 {
90 	u64 a = of_read_number(addr, na);
91 	memset(addr, 0, na * 4);
92 	a += offset;
93 	if (na > 1)
94 		addr[na - 2] = cpu_to_fdt32(a >> 32);
95 	addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
96 
97 	return 0;
98 }
99 
100 /* Array of bus specific translators */
101 static const struct of_bus of_busses[] __initconst = {
102 	/* Default */
103 	{
104 		.count_cells = fdt_bus_default_count_cells,
105 		.map = fdt_bus_default_map,
106 		.translate = fdt_bus_default_translate,
107 	},
108 };
109 
fdt_translate_one(const void * blob,int parent,const struct of_bus * bus,const struct of_bus * pbus,__be32 * addr,int na,int ns,int pna,const char * rprop)110 static int __init fdt_translate_one(const void *blob, int parent,
111 				    const struct of_bus *bus,
112 				    const struct of_bus *pbus, __be32 *addr,
113 				    int na, int ns, int pna, const char *rprop)
114 {
115 	const __be32 *ranges;
116 	int rlen;
117 	int rone;
118 	u64 offset = OF_BAD_ADDR;
119 
120 	ranges = fdt_getprop(blob, parent, rprop, &rlen);
121 	if (!ranges)
122 		return 1;
123 	if (rlen == 0) {
124 		offset = of_read_number(addr, na);
125 		memset(addr, 0, pna * 4);
126 		pr_debug("FDT: empty ranges, 1:1 translation\n");
127 		goto finish;
128 	}
129 
130 	pr_debug("FDT: walking ranges...\n");
131 
132 	/* Now walk through the ranges */
133 	rlen /= 4;
134 	rone = na + pna + ns;
135 	for (; rlen >= rone; rlen -= rone, ranges += rone) {
136 		offset = bus->map(addr, ranges, na, ns, pna);
137 		if (offset != OF_BAD_ADDR)
138 			break;
139 	}
140 	if (offset == OF_BAD_ADDR) {
141 		pr_debug("FDT: not found !\n");
142 		return 1;
143 	}
144 	memcpy(addr, ranges + na, 4 * pna);
145 
146  finish:
147 	of_dump_addr("FDT: parent translation for:", addr, pna);
148 	pr_debug("FDT: with offset: %llx\n", offset);
149 
150 	/* Translate it into parent bus space */
151 	return pbus->translate(addr, offset, pna);
152 }
153 
154 /*
155  * Translate an address from the device-tree into a CPU physical address,
156  * this walks up the tree and applies the various bus mappings on the
157  * way.
158  *
159  * Note: We consider that crossing any level with #size-cells == 0 to mean
160  * that translation is impossible (that is we are not dealing with a value
161  * that can be mapped to a cpu physical address). This is not really specified
162  * that way, but this is traditionally the way IBM at least do things
163  */
fdt_translate_address(const void * blob,int node_offset)164 u64 __init fdt_translate_address(const void *blob, int node_offset)
165 {
166 	int parent, len;
167 	const struct of_bus *bus, *pbus;
168 	const __be32 *reg;
169 	__be32 addr[OF_MAX_ADDR_CELLS];
170 	int na, ns, pna, pns;
171 	u64 result = OF_BAD_ADDR;
172 
173 	pr_debug("FDT: ** translation for device %s **\n",
174 		 fdt_get_name(blob, node_offset, NULL));
175 
176 	reg = fdt_getprop(blob, node_offset, "reg", &len);
177 	if (!reg) {
178 		pr_err("FDT: warning: device tree node '%s' has no address.\n",
179 			fdt_get_name(blob, node_offset, NULL));
180 		goto bail;
181 	}
182 
183 	/* Get parent & match bus type */
184 	parent = fdt_parent_offset(blob, node_offset);
185 	if (parent < 0)
186 		goto bail;
187 	bus = &of_busses[0];
188 
189 	/* Cound address cells & copy address locally */
190 	bus->count_cells(blob, parent, &na, &ns);
191 	if (!OF_CHECK_COUNTS(na, ns)) {
192 		pr_err("FDT: Bad cell count for %s\n",
193 		       fdt_get_name(blob, node_offset, NULL));
194 		goto bail;
195 	}
196 	memcpy(addr, reg, na * 4);
197 
198 	pr_debug("FDT: bus (na=%d, ns=%d) on %s\n",
199 		 na, ns, fdt_get_name(blob, parent, NULL));
200 	of_dump_addr("OF: translating address:", addr, na);
201 
202 	/* Translate */
203 	for (;;) {
204 		/* Switch to parent bus */
205 		node_offset = parent;
206 		parent = fdt_parent_offset(blob, node_offset);
207 
208 		/* If root, we have finished */
209 		if (parent < 0) {
210 			pr_debug("FDT: reached root node\n");
211 			result = of_read_number(addr, na);
212 			break;
213 		}
214 
215 		/* Get new parent bus and counts */
216 		pbus = &of_busses[0];
217 		pbus->count_cells(blob, parent, &pna, &pns);
218 		if (!OF_CHECK_COUNTS(pna, pns)) {
219 			pr_err("FDT: Bad cell count for %s\n",
220 				fdt_get_name(blob, node_offset, NULL));
221 			break;
222 		}
223 
224 		pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n",
225 			 pna, pns, fdt_get_name(blob, parent, NULL));
226 
227 		/* Apply bus translation */
228 		if (fdt_translate_one(blob, node_offset, bus, pbus,
229 					addr, na, ns, pna, "ranges"))
230 			break;
231 
232 		/* Complete the move up one level */
233 		na = pna;
234 		ns = pns;
235 		bus = pbus;
236 
237 		of_dump_addr("FDT: one level translation:", addr, na);
238 	}
239  bail:
240 	return result;
241 }
242